This notebook is a part of a group collaboration with University of Virginia students, professors, and SmartCville. that looks at the affordability of single-family housing in the City of Charlottesville over the past 20 years. Hopefully, this project will gain insights into how difficult or easy it has been to obtain a single-family property across the City.
The point of this study will be to observe housing prices in the City of Charlottesville. The following packages are imported. Numpy, geopandas, and pandas are used for data handling. Folium is used for data visualization; it is a Python wrapper for the Javascript library leaflet.
import numpy as np
import pandas as pd
import geopandas as gpd
import folium
from folium import plugins
import json
import datetime
import branca
from sklearn.preprocessing import MinMaxScaler
import plotly.express as ply
#These lines set up the plotting functionality and formatting.
import matplotlib
#matplotlib.use('Agg', warn=False)
#%matplotlib inline
import matplotlib.pyplot as plots
#plots.style.use('fivethirtyeight')
# import warnings
# warnings.simplefilter(action="ignore", category=FutureWarning)
Municipal Boundary Area: https://opendata.charlottesville.org/datasets/municipal-boundary-area
The Folium module adapts Leaflet maps to Python. The provided coordinates (location=38.0393, -78.4767) will be used to generate all subsequent maps of the City of Charlottesville. Cville Open Data provides an approximate municipal boundary area in the form of a GeoJson/Json layer. The houses in this study are found within this boundary. Folium can take in GeoJson layers automatically as a layer.
with open ("data/cityboundary.geojson") as f:
citylayer = json.load(f)
city_cville_coord=[38.0393, -78.4767]
map_with_citylayer = folium.Map(location=city_cville_coord, zoom_start=13)
folium.GeoJson(citylayer).add_to(map_with_citylayer)
map_with_citylayer
The repository https://opendata.charlottesville.org/ offers data on various utilities in the City of Charlottesville. The previous section's Municipal Bounday Area is part of the Property subsection, which has various datasets. This section will extract the information that is useful for this study, formatting relevant portions of each table so that they are ready for Folium.
Source: https://opendata.charlottesville.org/datasets/real-estate-sales
The Sales Table contains data about sales of particular parcels. It contains their addresses, date of sale, and amount of sale. sales is a DataFrame that reads in this csv.
sales = pd.read_csv('data/Real_Estate__Sales_.csv')
sales
| RecordID_Int | ParcelNumber | SaleDate | SaleAmount | StreetName | StreetNumber | Unit | |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 010001000 | 2000/10/23 00:00:00+00 | 0 | EMMET ST N | 1117 | NaN |
| 1 | 2 | 010001000 | 2016/02/10 00:00:00+00 | 0 | EMMET ST N | 1117 | NaN |
| 2 | 3 | 010001100 | 2008/09/23 00:00:00+00 | 0 | MILLMONT ST | 1035-67 | NaN |
| 3 | 4 | 010001100 | 2008/09/23 00:00:00+00 | 0 | MILLMONT ST | 1035-67 | NaN |
| 4 | 5 | 010001100 | 2008/09/23 00:00:00+00 | 0 | MILLMONT ST | 1035-67 | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 56291 | 56277 | 610318000 | 2016/12/05 00:00:00+00 | 0 | MILFORD TER | 110 | NaN |
| 56292 | 56278 | 610318000 | 1999/10/21 00:00:00+00 | 81500 | MILFORD TER | 110 | NaN |
| 56293 | 56279 | 610318000 | 2017/03/07 00:00:00+00 | 148000 | MILFORD TER | 110 | NaN |
| 56294 | 56280 | 610318000 | 2003/03/03 00:00:00+00 | 116900 | MILFORD TER | 110 | NaN |
| 56295 | 56281 | 610318000 | 2013/01/22 00:00:00+00 | 0 | MILFORD TER | 110 | NaN |
56296 rows × 7 columns
The Street Number and Street Address are combined for ease of use later on. The Address column represents both of them. The two separate components to this address are then dropped.
sales['Address'] = sales['StreetNumber'] + ' '+sales['StreetName']
sales = sales.drop(columns=['StreetNumber','StreetName'])
sales
| RecordID_Int | ParcelNumber | SaleDate | SaleAmount | Unit | Address | |
|---|---|---|---|---|---|---|
| 0 | 1 | 010001000 | 2000/10/23 00:00:00+00 | 0 | NaN | 1117 EMMET ST N |
| 1 | 2 | 010001000 | 2016/02/10 00:00:00+00 | 0 | NaN | 1117 EMMET ST N |
| 2 | 3 | 010001100 | 2008/09/23 00:00:00+00 | 0 | NaN | 1035-67 MILLMONT ST |
| 3 | 4 | 010001100 | 2008/09/23 00:00:00+00 | 0 | NaN | 1035-67 MILLMONT ST |
| 4 | 5 | 010001100 | 2008/09/23 00:00:00+00 | 0 | NaN | 1035-67 MILLMONT ST |
| ... | ... | ... | ... | ... | ... | ... |
| 56291 | 56277 | 610318000 | 2016/12/05 00:00:00+00 | 0 | NaN | 110 MILFORD TER |
| 56292 | 56278 | 610318000 | 1999/10/21 00:00:00+00 | 81500 | NaN | 110 MILFORD TER |
| 56293 | 56279 | 610318000 | 2017/03/07 00:00:00+00 | 148000 | NaN | 110 MILFORD TER |
| 56294 | 56280 | 610318000 | 2003/03/03 00:00:00+00 | 116900 | NaN | 110 MILFORD TER |
| 56295 | 56281 | 610318000 | 2013/01/22 00:00:00+00 | 0 | NaN | 110 MILFORD TER |
56296 rows × 6 columns
formatDate(row) is an applied function that takes every row of the DataFrame and modifies the SaleDate. The time portion is removed since none of the dates appear to specify an actual time. The backslashes are replaced with dashes to better coincide with numpy's date objects. sales now removes rows that do not have proper dates, and applies the formatDate(row) function. Comparing DF shapes, only one appeared to have an invalid date. Finally, the SalesDate column is converted into a pandas DateTime object.
def formatDate(row):
date = row['SaleDate']
return date[:10].replace('/','-')
sales = sales[~pd.isna(sales.SaleDate)]
sales = sales.assign(SaleDate= sales.apply(formatDate,axis=1))
#sales['SaleDate'] = pd.DatetimeIndex(sales['SaleDate'])
sales
| RecordID_Int | ParcelNumber | SaleDate | SaleAmount | Unit | Address | |
|---|---|---|---|---|---|---|
| 0 | 1 | 010001000 | 2000-10-23 | 0 | NaN | 1117 EMMET ST N |
| 1 | 2 | 010001000 | 2016-02-10 | 0 | NaN | 1117 EMMET ST N |
| 2 | 3 | 010001100 | 2008-09-23 | 0 | NaN | 1035-67 MILLMONT ST |
| 3 | 4 | 010001100 | 2008-09-23 | 0 | NaN | 1035-67 MILLMONT ST |
| 4 | 5 | 010001100 | 2008-09-23 | 0 | NaN | 1035-67 MILLMONT ST |
| ... | ... | ... | ... | ... | ... | ... |
| 56291 | 56277 | 610318000 | 2016-12-05 | 0 | NaN | 110 MILFORD TER |
| 56292 | 56278 | 610318000 | 1999-10-21 | 81500 | NaN | 110 MILFORD TER |
| 56293 | 56279 | 610318000 | 2017-03-07 | 148000 | NaN | 110 MILFORD TER |
| 56294 | 56280 | 610318000 | 2003-03-03 | 116900 | NaN | 110 MILFORD TER |
| 56295 | 56281 | 610318000 | 2013-01-22 | 0 | NaN | 110 MILFORD TER |
56295 rows × 6 columns
Source: https://opendata.charlottesville.org/datasets/real-estate-residential-details
The Residential datasheet contains a list of all the residential parcels of the area. This is important to filter out the non-residential parcels located in the Sales dataset. The datasheet has a column called "UseCode" that specifies the type of residential building in the parcel. It was discovered that some of the labels in the column were not relevant to family housing (such as parking lots and vacant lands). Filtering was done to remove these parcels from the dataset, and of the fifty or so labels, about ten are left.
resid is the DataFrame after reading in this Residential csv.
resid = pd.read_csv('data/Real_Estate__Residential_Details_.csv',header=0)
resid
| RecordID_Int | ParcelNumber | StreetNumber | StreetName | Unit | UseCode | Style | Grade | Roof | Flooring | ... | Bedrooms | HalfBathrooms | FullBathrooms | BasementGarage | Basement | FinishedBasement | BasementType | ExternalWalls | NumberOfStories | SquareFootageFinishedLiving | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 010001600 | 600 | MASSIE RD | NaN | Multipurpose Bldg (College) | 2 Story | A | Metal | None | ... | 4 | 0 | 4 | 0 | 0.0 | 0.0 | No Basement | Wood | 2.00 | 3672.0 |
| 1 | 2 | 010005000 | 0 | BARRACKS RD | NaN | Vacant Land | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | 0.0 | 0.0 | NaN | NaN | NaN | NaN |
| 2 | 3 | 010006000 | 2028 | BARRACKS RD | NaN | Single Family | 2 Story | A | Architectural Shingles | Hardwood | ... | 6 | 1 | 4 | 0 | 1018.0 | 900.0 | Partial Basement | Brick Veneer | 2.00 | 3368.0 |
| 3 | 4 | 010007000 | 1930 | BARRACKS RD | NaN | Single Family | 2 Story | A | Slate | Hardwood | ... | 5 | 1 | 4 | 0 | 1708.0 | 1468.0 | Partial Basement | Brick Veneer | 2.00 | 5446.0 |
| 4 | 5 | 010008000 | 0 | BARRACKS RD | NaN | Vacant Land | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | 0.0 | 0.0 | NaN | NaN | NaN | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 14669 | 14674 | 610314000 | 102 | MILFORD TER | NaN | Single Family Attached | 2 Sty Attached | C | Architectural Shingles | W/W Carpet | ... | 3 | 1 | 1 | 0 | NaN | NaN | No Basement | Vinyl | 2.00 | 1200.0 |
| 14670 | 14675 | 610315000 | 104 | MILFORD TER | NaN | Single Family Attached | 2 Sty Attached | C | Architectural Shingles | W/W Carpet | ... | 3 | 1 | 2 | 0 | NaN | NaN | No Basement | Vinyl | 2.00 | 1200.0 |
| 14671 | 14676 | 610316000 | 106 | MILFORD TER | NaN | Single Family Attached | 2 Sty Attached | C | Architectural Shingles | W/W Carpet | ... | 3 | 1 | 1 | 0 | NaN | NaN | No Basement | Vinyl | 2.00 | 1200.0 |
| 14672 | 14677 | 610317000 | 108 | MILFORD TER | NaN | Single Family Attached | 2 Sty Attached | C | Architectural Shingles | W/W Carpet | ... | 3 | 1 | 1 | 0 | NaN | NaN | No Basement | Vinyl | 2.00 | 1200.0 |
| 14673 | 14678 | 610318000 | 110 | MILFORD TER | NaN | Single Family Attached | 1.5 Sty Attached | C | Architectural Shingles | W/W Carpet | ... | 3 | 0 | 2 | 0 | NaN | NaN | No Basement | Vinyl | 1.75 | 1080.0 |
14674 rows × 24 columns
salesResid is the result of merging the sales and resid. Only parcels of UseCodes in the set labels are included. The join is done using the ParcelNumber from each dataframe.
# Filter out non-residential parcels
labels = {'Single Family', 'Duplex', 'Single Family Attached', 'Condominium',
'Single Family-1 Conversion', 'Triplex', 'Rooming House', 'Condo Main',
'Single Family-3 Conversion', 'Four-Level Split', 'Three-Level Split',
'Single Family-2 Conversion', 'Condominium-Flex', 'Quadplex'}
salesResid = pd.merge(sales,resid[resid.UseCode.isin(labels)].loc[:,['ParcelNumber','UseCode']],on="ParcelNumber")
salesResid
| RecordID_Int | ParcelNumber | SaleDate | SaleAmount | Unit | Address | UseCode | |
|---|---|---|---|---|---|---|---|
| 0 | 60 | 010006000 | 1995-05-25 | 0 | NaN | 2028 BARRACKS RD | Single Family |
| 1 | 61 | 010006000 | 2003-02-20 | 0 | NaN | 2028 BARRACKS RD | Single Family |
| 2 | 62 | 010006000 | 2003-01-31 | 545000 | NaN | 2028 BARRACKS RD | Single Family |
| 3 | 63 | 010007000 | 1982-07-27 | 160000 | NaN | 1930 BARRACKS RD | Single Family |
| 4 | 66 | 010009000 | 1984-08-31 | 175000 | NaN | 1920 BARRACKS RD | Single Family |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 48052 | 56277 | 610318000 | 2016-12-05 | 0 | NaN | 110 MILFORD TER | Single Family Attached |
| 48053 | 56278 | 610318000 | 1999-10-21 | 81500 | NaN | 110 MILFORD TER | Single Family Attached |
| 48054 | 56279 | 610318000 | 2017-03-07 | 148000 | NaN | 110 MILFORD TER | Single Family Attached |
| 48055 | 56280 | 610318000 | 2003-03-03 | 116900 | NaN | 110 MILFORD TER | Single Family Attached |
| 48056 | 56281 | 610318000 | 2013-01-22 | 0 | NaN | 110 MILFORD TER | Single Family Attached |
48057 rows × 7 columns
The addresses in the table were then geocoded (found latitude/longitude coordinates using addresses) and located in a .csv file called 'coordinates.csv'. This was merged with the combined salesResid table. geoSalesResid is the table that will be used for subsequent sections. As a baseline, parcels with SaleAmounts less than 100 were assumed to be typos or invalid sales and filtered out. Additionally, it was found that some parcels had sales with different 'RecordID', but with the same exact 'SaleDate', 'SaleAmount', and 'ParcelNumber'. These would also be filtered out.
geocoded = pd.read_csv('data/coordinates.csv')
geoSalesResid = pd.merge(salesResid,geocoded,left_on="Address",right_on="ADDRESS")
geoSalesResid = geoSalesResid[geoSalesResid.SaleAmount > 100].drop(columns=["ADDRESS"])
geoSalesResid = geoSalesResid.drop_duplicates(subset=['ParcelNumber','SaleDate','SaleAmount'])
geoSalesResid
| RecordID_Int | ParcelNumber | SaleDate | SaleAmount | Unit | Address | UseCode | LATITUDE | LONGITUDE | |
|---|---|---|---|---|---|---|---|---|---|
| 2 | 62 | 010006000 | 2003-01-31 | 545000 | NaN | 2028 BARRACKS RD | Single Family | 38.050765 | -78.498080 |
| 3 | 63 | 010007000 | 1982-07-27 | 160000 | NaN | 1930 BARRACKS RD | Single Family | 38.050395 | -78.497636 |
| 4 | 66 | 010009000 | 1984-08-31 | 175000 | NaN | 1920 BARRACKS RD | Single Family | 38.049731 | -78.497104 |
| 7 | 69 | 010010000 | 2018-10-31 | 575000 | NaN | 1851 WESTVIEW RD | Single Family | 38.048508 | -78.498668 |
| 8 | 70 | 010011000 | 2018-01-10 | 970000 | NaN | 1855 WESTVIEW RD | Single Family | 38.049037 | -78.498346 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 48019 | 56275 | 610318000 | 2001-07-20 | 89900 | NaN | 110 MILFORD TER | Single Family Attached | 38.018268 | -78.470688 |
| 48020 | 56276 | 610318000 | 1999-02-18 | 93000 | NaN | 110 MILFORD TER | Single Family Attached | 38.018268 | -78.470688 |
| 48022 | 56278 | 610318000 | 1999-10-21 | 81500 | NaN | 110 MILFORD TER | Single Family Attached | 38.018268 | -78.470688 |
| 48023 | 56279 | 610318000 | 2017-03-07 | 148000 | NaN | 110 MILFORD TER | Single Family Attached | 38.018268 | -78.470688 |
| 48024 | 56280 | 610318000 | 2003-03-03 | 116900 | NaN | 110 MILFORD TER | Single Family Attached | 38.018268 | -78.470688 |
30618 rows × 9 columns
The goal of this section is to begin making visualizations of houses and their sale prices.
makeRecentSales(cutoffDate) is a function that creates a subset of geoSalesResid based on a cutoff date. It will only look at sales that have happened after the specified date. recentSales is an example DataFrame created that has sales from the beginning of 2019 to the end of 2019. The end of 2019 was chosen to be the standard end date since 2020 has not as of yet finished.
def makeRecentSales(cutoffDate):
return geoSalesResid[(geoSalesResid.SaleDate > cutoffDate) & (geoSalesResid.SaleDate < '2020-01-01')]
recentSales = makeRecentSales('2018-12-31')
A Folium map can be generated with circle markers describing a coordinate that has latitude and longitude. addMarkersSale is an applied function that will read the rows of the DataFrame and add a circle marker to it. The color will be chosen using the chooseColor function which defines the sale cutoffs that will be used on the map. It uses the Yellow/Orange/Red color brewer gradient. The markers will also show the Sale Date and Amount for each. formatSalesNumber(v) is a helper function that adds commas every third digit in the sale.
def formatSalesNumber(v):
# split it between left/right of decimal point
splitstr = v.split('.')
# left num is first entry of split list, then count number of digits it has
leftNum = splitstr[0]
leftNumDigits = len(splitstr[0])
# if there exists num right of point, then set it to right Num
if len(splitstr) > 1:
rightNum = '.' + splitstr[1]
else:
rightNum = '.00'
# Count number of commas necessary
comma = 0
if leftNumDigits > 3:
temp = leftNumDigits
while(((temp -1) // 3) > 0):
temp -= 3
comma += 1
else:
return '$' + leftNum + rightNum
# Produce the string
if leftNumDigits % 3 == 0:
s = '$' + leftNum[:3]
for i in np.arange(comma):
a = i + 1
s += ','+ leftNum[3 * a:3 * a + 3]
return s + rightNum
else:
s = '$' + leftNum[:(leftNumDigits % 3)]
for i in np.arange(comma):
a = i + 1
b = leftNumDigits % 3
s += ','+ leftNum[b * a : b * a + 3]
return s + rightNum
def chooseColorSale(sale):
if sale < 89000:
return '#fef0d9'
elif 89000<= sale < 150000:
return '#fdd49e'
elif 150000 <= sale < 300000:
return '#fdbb84'
elif 300000 <= sale < 500000:
return '#fc8d59'
elif 500000 <= sale < 800000:
return '#e34a33'
else:
return '#b30000'
def addMarkersSale(row,currmap):
line = row["SaleDate"][:] + ": " + formatSalesNumber(str(row['SaleAmount']))
folium.CircleMarker(location=[row['LATITUDE'],row['LONGITUDE']],
popup=line,radius=1.5,color=chooseColorSale(row['SaleAmount']),
fill_color=chooseColorSale(row['SaleAmount'])).add_to(currmap)
return
A Folium map is created, centered on Charlottesville. generateSalesMap() is just a function that encloses this process of generating a map. The functions described above are used to add circle markers to every parcel that had a Sale from beginning of 2019 to the present.
def generateSalesMap():
m = folium.Map(location=city_cville_coord, zoom_start=13)
makeRecentSales('2018-12-31').apply(addMarkersSale,axis=1,args=(m,))
return m
generateSalesMap()
Planning Neighborhood Area: https://opendata.charlottesville.org/datasets/planning-neighborhood-area
The goal of this section is to make use of special zones in the City. The Neighborhod Development Services (NDS) createad a Cville OpenData file that lists "Planning Neighborhood Areas". Using this and the points-in-polygon problem, each property will now be able to be classified into a planning neighborhood area based on their latitude and longitude coordinates obtained from section 2.3.
From the map in the previous section, it can be seen that there are exist clusters of similar sale prices. JSON object files are a good way to represent geographical areas. Cville Open Data provides a set of objects called Planning Neighborhood Areas, and said set is used as a layer. generateJSONMap() produces a map of Charlottesville with these Planning Areas overlayed.
def generateJSONMap():
m = folium.Map(location=city_cville_coord, zoom_start=13)
folium.GeoJson("data/regions.geojson", name='area').add_to(m)
return m
generateJSONMap()
The GeoJson file has a lot of additional information for each layer. The latitude/longitude of each layer are extracted from this. The variable lay is a list of coordinate lists.
layoutFile = json.load(open('data/regions.geojson'))
arr = []
for i in np.arange(len(layoutFile['features'])):
arr.append(layoutFile['features'][i]['geometry']['coordinates'][0])
lay = pd.Series(arr)
To combine these individual parcel points with the imported JSON layer into a cohesive visualization, the Point in Polygon problem must be solved. How can we automate the process of determining which region a house belongs to? The next section will describe the algorithm that ultimately determines whether a parcel (point) is located within a particular JSON object (polygon).
With the imported JSON polygon layers, each point in the previous section should be grouped with each polygon. This is a point in polygon problem: determine whether or not a point is in a polygon defined by its vertices (set of points).
A way to solve this problem is with ray-casting. Take a point and extend it "infinitely" along an arbitrary direction (it is now a ray). Count the number of times the point intersects a side of the polygon. If the number of ray intersections is even, then it is not in the polygon. If the number of ray intersections is odd, then it is in the polygon.
The first issue is, given two pairs of points (that define two line segments), determine if they intersect or not. The solution is to check the orientation of these points (intersecting line segments should have their respective points in between one point from the other line segment). A algorithm written by Kite is used. The main function below is intersects(s1,s2) and it takes in two tuples.
Note: All credit for the functions _onsegment(p,q,r), orientation(p,q,r), and intersects(s1,s2) goes to Kite [https://www.kite.com/python/answers/how-to-check-if-two-line-segments-intersect-in-python].
def on_segment(p,q,r):
if r[0] <= max(p[0], q[0]) and r[0] >= min(p[0], q[0]) and r[1] <= max(p[1], q[1]) and r[1] >= min(p[1], q[1]):
return True
else:
return False
def orientation(p,q,r):
val = ((q[1] - p[1]) * (r[0] - q[0])) - ((q[0] - p[0]) * (r[1] - q[1]))
if val == 0:
return 0
elif val > 0:
return 1
else:
return -1
def intersects(s1,s2):
p1,q1 =s1
p2,q2 = s2
o1 = orientation(p1,q1,p2)
o2 = orientation(p1,q1,q2)
o3 = orientation(p2,q2,p1)
o4 = orientation(p2,q2,q1)
if o1 != o2 and o3 != o4:
return True
if o1 == 0 and on_segment(p1,q1,p2):
return True
if o2 == 0 and on_segment(p1, q1, q2):
return True
if o3 == 0 and on_segment(p2, q2, p1):
return True
if o4 == 0 and on_segment(p2, q2, q1):
return True
return False
Now the point in polygon algorithm can be applied for this scenario.
The main function is pointinpolygons(row,layout), which will be applied across the entire table with each row (point) as its argument. It will return a series of booleans showing whether or not the point is in each of the polygons in the JSON file.
The makeray(point) function receives a point and returns a sequence with two tuples inside that represent the ray. For this, each point is extended horizontally. Additionally, since all points reside in Charlottesville, the left/right longitude are the west-most/east-most points of the area, respectively.
The pointinpolygon(polygons,raysegment) function is the individual function that is applied inside pointinpolygons. It creates a list of tuples that contain the line segments of the polygon. It is assumed that the Json file is configured so that the points can be connected from smallest to biggest (index 0 to index 1, index 1 to index 2, etc) until the very last which is connected back to index 0. It then counts the number of intersections as described above about the ray-casting.
# defines a point, returns a series of whether or not point is in each JSON polygon
def pointinpolygons(row,layout):
pt = (row['LATITUDE'],row['LONGITUDE'])
rayseg = makeray(pt)
foundin = layout.apply(pointinpoly,args=(rayseg,))
if foundin[foundin].shape[0] == 0:
return 0
else:
return foundin[foundin].index[0] + 1
# produces a ray by extending a point horizontally depending on its longitude relative to Cville
def makeray(point):
# Bounds for Longitude
leftb = -78.647930
rightb = -78.411250
midb = leftb + (rightb-leftb)/2
if point[1] > midb:
return (point,(point[0],leftb))
else:
return (point,(point[0],rightb))
# determines if the point is in or not in a single JSON polygon
def pointinpoly(polygons,raysegment):
# Makes pairs of indices to form polygon edges (assumes that edges are defined as) from a polygon that has n sides
# 0 to 1, 1 to 2, 2 to 3, etc... until the last index which would be n-1 to 0
combo = []
for i in np.arange(len(polygons)):
if i < len(polygons) - 1:
apair = (i,i+1)
else:
apair = (i,0)
combo.append(apair)
# Iterates through the list of pairs and indexes into the polygons variable which is a list of lists. It produces a
# a segment represented by two points and it is determined whether it intersects with the address.
intersections = 0
for pair in combo:
segment1 = ((polygons[pair[0]][1],polygons[pair[0]][0]), (polygons[pair[1]][1],polygons[pair[1]][0]))
if intersects(segment1,raysegment):
intersections +=1
if intersections % 2 == 0:
return False
else:
return True
The function is used to insert a column that assigns each point to its polygon region. If it is not in any of the polygons, then it is assigned 0. The recentSales table that contained sales after 2018 adds a new column that categorizes each parcel into an area.
recentSales.insert(recentSales.shape[1],'Region',recentSales.apply(pointinpolygons,axis=1,args=(lay,)))
To verify the accuracy of the algorithm, the same table is used to create another map with the Json layers. Filtering out those without a region, there does not seem to be a point that lies outside the polygons. generateSalesMapWithJson() uses recentSales and filters out those without a region (classifed with 0). In fact, there does not appear to be any house that is actually part of region 0, meaning that none in the dataset were outside of the City of Cville area.
def generateSalesMapWithJson():
m = folium.Map(location=city_cville_coord, zoom_start=13)
testTab = recentSales[recentSales.Region >0].apply(addMarkersSale,axis=1,args=(m,))
folium.GeoJson("data/regions.geojson", name='area').add_to(m)
return m
generateSalesMapWithJson()
The pandas group function can be used to group all the points by region and determine the average sale amount within that region. salesByRegion is an example of grouping the entire recentSales table by region and finding the average of their sale prices to form a Series.
salesByRegion = recentSales.groupby(['Region']).mean().loc[:,"SaleAmount"]
salesByRegion
Region 1 717857.860465 2 511822.775000 3 314556.120000 4 368137.540230 5 441875.581395 6 200313.800000 7 314220.600000 8 241635.603448 9 357772.259259 10 292720.743590 11 750000.000000 12 105000.000000 13 328184.814516 14 358465.073171 15 767669.529412 16 586843.750000 17 278134.142857 18 207666.666667 19 563986.363636 Name: SaleAmount, dtype: float64
def classifyHouses(df):
df.insert(df.shape[1],'Region',df.apply(pointinpolygons,axis=1,args=(lay,)))
df.to_csv('data/salesLast20Years.csv')
return
run_already = True
if not run_already:
classifyHouses(makeRecentSales('1999-12-31'))
A style function tells Folium how to format the JSON layers onto the map. This is necessary to customize GeoJSON layers in Folium. The color tiers similarily follow the function chooseColorSale() in section 3.
def stylefunction(x):
sale=x['properties']['SaleAverage']
if sale < 89000:
color= '#fef0d9'
elif 89000<= sale < 150000:
color= '#fdd49e'
elif 150000 <= sale < 300000:
color= '#fdbb84'
elif 300000 <= sale < 500000:
color= '#fc8d59'
elif 500000 <= sale < 800000:
color= '#e34a33'
else:
color= '#b30000'
return {'weight': 5, 'color': color,'fill': True, 'fillOpacity':0.5}
Some helper functions are created to ensure that the main map generation function is not too cluttered.
def produceAGroup(t,func,label):
if func == 'mean':
temp = t.groupby(['Region']).mean().loc[:,label]
return pd.DataFrame({'Regions': pd.Series(data=np.arange(20)), 'Avg': temp}).fillna(value=0)
elif func == 'median':
temp = t.groupby(['Region']).median().loc[:,label]
return pd.DataFrame({'Regions': pd.Series(data=np.arange(20)), 'Med': temp}).fillna(value=0)
elif func == 'count':
temp = t.groupby(['Region']).count().iloc[:,0]
return pd.DataFrame({'Regions': pd.Series(data=np.arange(20)), 'Count': temp}).fillna(value=0)
def checkIfZero(s,l,n):
try:
return str(round(s[l][n],2))
except:
return '0'
def addMarkersSaleGroup(row,currmap,group):
# normalized to day (percNorm)
price = row['SaleAmount']
line = row ["UseCode"] + '(' + row["SaleDate"][:4] + "): " + formatSalesNumber(str(row['SaleAmount']))
marker = folium.CircleMarker(location=[row['LATITUDE'],row['LONGITUDE']],
popup=line,radius=1.5,color=chooseColorSale(price),
fill_color=chooseColorSale(price))
group.add_child(marker)
return
The generateSalesMap(salesTable) function will do the following:
def generateSalesMap():
# 1. Import the saved Table
salesTable = pd.read_csv('data/salesLast20Years.csv')
# 2. Drop the redundant column
salesTable = salesTable.drop(columns='Unnamed: 0')
# 3. Group by region number and find average sale amount and median in each, even for region 0. Produce a DataFrame that has
# the region numbers as a column
salesByRegionAvg = produceAGroup(salesTable,'mean','SaleAmount')
salesByRegionMed = produceAGroup(salesTable,'median','SaleAmount')
salesByRegionCount = produceAGroup(salesTable,'count','SaleAmount')
# 4. load GeoJSON file of regions, adding the SaleAverage as a property to each
with open ("data/regions.geojson") as f:
regionlayer = json.load(f)
count = 1
for i in regionlayer['features']:
i['properties']['AVG PRICE'] = formatSalesNumber(checkIfZero(salesByRegionAvg,'Avg',count))
i['properties']['MED PRICE'] = formatSalesNumber(checkIfZero(salesByRegionMed,'Med',count))
i['properties']['NUM SALES'] = salesByRegionCount.Count[count]
count += 1
# 5. Load GeoJSON of city boundary, adding SaleAverage/Median/Count of whole city to the Layer
with open ("data/cityboundary.geojson") as f:
citylayer = json.load(f)
citylayer['features'][0]['properties']['AVG PRICE'] = formatSalesNumber(str(round(salesTable.SaleAmount.mean(),2)))
citylayer['features'][0]['properties']['MED PRICE'] = formatSalesNumber(str(round(salesTable.SaleAmount.median(),2)))
citylayer['features'][0]['properties']['NUM SALES'] = salesTable.shape[0]
# 6. Create Folium Map and add TileLayer Options
m = plugins.DualMap(location=city_cville_coord, zoom_start=13)
folium.raster_layers.TileLayer('Open Street Map').add_to(m)
folium.raster_layers.TileLayer('Stamen Toner').add_to(m)
folium.raster_layers.TileLayer('CartoDB Positron').add_to(m)
# 7. Group: GeoJson of Charlottesville City Boundary
cityavg = salesTable.SaleAmount.mean()
style_function = lambda x: {'fillColor': chooseColorSale(cityavg), 'color': chooseColorSale(cityavg)}
city = folium.GeoJson(citylayer, name='City of Charlottesville Boundary',
style_function=style_function,
tooltip=folium.GeoJsonTooltip(['NAME','AVG PRICE','MED PRICE', 'NUM SALES'])).add_to(m)
# 8. Group: Addition of Markers for each individual Parcel
saleMarkers = folium.FeatureGroup(name='Sale Price Markers',show=False)
salesTable.apply(addMarkersSaleGroup,axis=1,args=(m,saleMarkers))
m.add_child(saleMarkers)
# 9. Group: Choropleth of Planning Neighborhood Regions' Average Sale
choroAvg = folium.Choropleth(geo_data=regionlayer, key_on='feature.properties.OBJECTID',
data=salesByRegionAvg,columns=['Regions','Avg'],
fill_color='YlOrRd',legend_name='Sale Amount ($): Average',name='Sale Amount Average', show=False,
highlight=True).add_to(m.m1)
choroAvg.geojson.add_child(folium.GeoJsonTooltip(['OBJECTID','NAME','AVG PRICE','NUM SALES']))
# 10. Group: Choropleth of Planning Neighborhood Regions' Median Sale
choroMed = folium.Choropleth(geo_data=regionlayer, key_on='feature.properties.OBJECTID',
data=salesByRegionMed,columns=['Regions','Med'],
fill_color='YlOrRd',legend_name='Sale Amount ($): Median',name='Sale Amount Median', show=False,
highlight=True).add_to(m.m2)
choroMed.geojson.add_child(folium.GeoJsonTooltip(['OBJECTID','NAME','MED PRICE','NUM SALES']))
# 11. Add LayerControl and Save Map
folium.LayerControl().add_to(m)
m.save("figures/map-sales.html")
return
generateSalesMap()
This section is just to separate the dual map into one that is mean and one that is median. This is to provide an alternative in case the dual, combined map appears to be too cluttered.
def generateSalesMapSeparate():
# 1. Import the saved Table
salesTable = pd.read_csv('data/salesLast20Years.csv')
# 2. Drop the redundant column
salesTable = salesTable.drop(columns='Unnamed: 0')
# 3. Group by region number and find average sale amount and median in each, even for region 0. Produce a DataFrame that has
# the region numbers as a column
salesByRegionAvg = produceAGroup(salesTable,'mean','SaleAmount')
salesByRegionMed = produceAGroup(salesTable,'median','SaleAmount')
salesByRegionCount = produceAGroup(salesTable,'count','SaleAmount')
# 4. load GeoJSON file of regions, adding the SaleAverage as a property to each
with open ("data/regions.geojson") as f:
regionlayer = json.load(f)
count = 1
for i in regionlayer['features']:
i['properties']['AVG PRICE'] = formatSalesNumber(checkIfZero(salesByRegionAvg,'Avg',count))
i['properties']['MED PRICE'] = formatSalesNumber(checkIfZero(salesByRegionMed,'Med',count))
i['properties']['NUM SALES'] = salesByRegionCount.Count[count]
count += 1
# 5. Load GeoJSON of city boundary, adding SaleAverage/Median/Count of whole city to the Layer
with open ("data/cityboundary.geojson") as f:
citylayer = json.load(f)
citylayer['features'][0]['properties']['AVG PRICE'] = formatSalesNumber(str(round(salesTable.SaleAmount.mean(),2)))
citylayer['features'][0]['properties']['MED PRICE'] = formatSalesNumber(str(round(salesTable.SaleAmount.median(),2)))
citylayer['features'][0]['properties']['NUM SALES'] = salesTable.shape[0]
# 6. Making Average Map
# Create Folium Map for averages and add TileLayer Options
m1 = folium.Map(location=city_cville_coord, zoom_start=13)
folium.raster_layers.TileLayer('Open Street Map').add_to(m1)
folium.raster_layers.TileLayer('Stamen Toner').add_to(m1)
folium.raster_layers.TileLayer('CartoDB Positron').add_to(m1)
# Group: GeoJson of Charlottesville City Boundary
cityavg = salesTable.SaleAmount.mean()
style_function = lambda x: {'fillColor': chooseColorSale(cityavg), 'color': chooseColorSale(cityavg)}
city = folium.GeoJson(citylayer, name='City of Charlottesville Boundary',
style_function=style_function,
tooltip=folium.GeoJsonTooltip(['NAME','AVG PRICE','NUM SALES'])).add_to(m1)
# Group: Addition of Markers for each individual Parcel
saleMarkers = folium.FeatureGroup(name='Sale Price Markers',show=False)
salesTable.apply(addMarkersSaleGroup,axis=1,args=(m1,saleMarkers))
m1.add_child(saleMarkers)
# Group: Choropleth of Planning Neighborhood Regions' Average Sale
choroAvg = folium.Choropleth(geo_data=regionlayer, key_on='feature.properties.OBJECTID',
data=salesByRegionAvg,columns=['Regions','Avg'],
fill_color='YlOrRd',legend_name='Sale Amount ($): Average',name='Sale Amount Average', show=False,
highlight=True).add_to(m1)
choroAvg.geojson.add_child(folium.GeoJsonTooltip(['OBJECTID','NAME','AVG PRICE','NUM SALES']))
# 7. Making Median Map
m2 = folium.Map(location=city_cville_coord, zoom_start=13)
folium.raster_layers.TileLayer('Open Street Map').add_to(m2)
folium.raster_layers.TileLayer('Stamen Toner').add_to(m2)
folium.raster_layers.TileLayer('CartoDB Positron').add_to(m2)
# 7. Group: GeoJson of Charlottesville City Boundary
cityavg = salesTable.SaleAmount.mean()
style_function = lambda x: {'fillColor': chooseColorSale(cityavg), 'color': chooseColorSale(cityavg)}
city = folium.GeoJson(citylayer, name='City of Charlottesville Boundary',
style_function=style_function,
tooltip=folium.GeoJsonTooltip(['NAME','MED PRICE','NUM SALES'])).add_to(m2)
# 8. Group: Addition of Markers for each individual Parcel
saleMarkers = folium.FeatureGroup(name='Sale Price Markers',show=False)
salesTable.apply(addMarkersSaleGroup,axis=1,args=(m2,saleMarkers))
m2.add_child(saleMarkers)
# 10. Group: Choropleth of Planning Neighborhood Regions' Median Sale
choroMed = folium.Choropleth(geo_data=regionlayer, key_on='feature.properties.OBJECTID',
data=salesByRegionMed,columns=['Regions','Med'],
fill_color='YlOrRd',legend_name='Sale Amount ($): Median',name='Sale Amount Median', show=False,
highlight=True).add_to(m2)
choroMed.geojson.add_child(folium.GeoJsonTooltip(['OBJECTID','NAME','MED PRICE','NUM SALES']))
# 11. Add LayerControl and Save Map
folium.LayerControl().add_to(m1)
folium.LayerControl().add_to(m2)
m1.save("figures/map-avg_sales.html")
m2.save("figures/map-med_sales.html")
return
generateSalesMapSeparate()
The previous map collected sales without regard for the actual sale date, as all the sales over the last 20 years were included. This section and the next look at trends over time.
Folium includes plugins from the Leaflet library, with some useful ones including time as a variable in map visualizations. This section will create visuals that can be varied through the last 20 years. Two plugins used are TimesliderChoropleth and HeatMapwithTime.
Pandas can easily group rows by certain values contained in certain columns. The last 20 years of sales will be imported and grouped by their year.
makeSalesTableByYear(t,y) is a helper function that returns a subset of the DataFrame in which only sales within a given range are included. plotyearlytrends() is a wrapper function that will import the dataframe of the 20 years of sales and then plot the average and median of each year's sales.
def makeSalesTableByYear(t,y):
start = str(y-1) + '-12-31'
end = str(y+1) + '01-01'
return t[(t.SaleDate > start) & (t.SaleDate < end)]
def plotyearlytrends():
df = pd.read_csv('data/salesLast20Years.csv')
df = df.drop(columns='Unnamed: 0')
avgsale = []
medsale = []
years = np.arange(2000,2020)
for i in years:
temp = makeSalesTableByYear(df,i)
avgsale.append(temp.SaleAmount.mean())
medsale.append(temp.SaleAmount.median())
df2 = pd.DataFrame([years,avgsale,medsale]).T
df2.columns = ['Year','Average', 'Median']
fig = ply.line(df2, x='Year',y=['Average','Median'])
fig.update_layout(
xaxis_title="Year",
yaxis_title="Sale Amount ($)")
fig.show()
fig.write_html("figures/plot-avgmedsales.html")
return
plotyearlytrends()
Now, it is time to look at each individual region per year. _mean_medbyregion() is a function that imports the saved tables of sales in the last 20 years (that includes which region it is located in). It will then group sales by the sale date year and by region and find the average and median of each group. This will then be inserted into two DataFrames that will be exported.
As seen from _avg_byregion, each region will have an average sale and median sale for every year from 2000 to 2020.
def mean_med_byregion():
avg_tab = pd.DataFrame(np.zeros((19,20)),index=np.arange(1,20),columns=[str(x) for x in np.arange(2000,2020)])
med_tab = avg_tab.copy()
table = pd.read_csv('data/salesLast20Years.csv')
table = table.drop(columns='Unnamed: 0')
for i in np.arange(2000,2020):
temp = makeSalesTableByYear(table,i)
avg_tab[str(i)] = temp.groupby(['Region']).mean().loc[:,'SaleAmount']
med_tab[str(i)] = temp.groupby(['Region']).median().loc[:,'SaleAmount']
avg_tab = avg_tab.fillna(0)
med_tab = med_tab.fillna(0)
return avg_tab,med_tab
avg_by_region, med_by_region = mean_med_byregion()
avg_by_region.T
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2000 | 191682.295918 | 163437.757143 | 94510.021739 | 1.393247e+05 | 190208.817308 | 147265.789474 | 343734.756944 | 81931.956522 | 134304.758621 | 79317.235294 | 66625.000000 | 63294.444444 | 140800.000000 | 113526.387097 | 291186.521739 | 176813.521739 | 64257.000000 | 5.794000e+04 | 203978.750000 |
| 2001 | 195886.862385 | 200869.136986 | 112521.763636 | 1.518983e+05 | 218400.581633 | 155957.352941 | 386299.730667 | 85457.993151 | 134239.923077 | 86603.740000 | 110785.714286 | 72625.000000 | 149693.870968 | 158362.333333 | 367338.333333 | 196208.068966 | 61366.666667 | 6.319655e+04 | 224007.747748 |
| 2002 | 229277.091743 | 200951.656716 | 172194.412500 | 1.786372e+05 | 247598.160920 | 150967.647059 | 123645.382838 | 99482.505435 | 167643.269231 | 166284.986111 | 187062.500000 | 79500.000000 | 173618.864865 | 190747.368421 | 413411.428571 | 199911.323077 | 72563.743590 | 7.517257e+04 | 256748.035714 |
| 2003 | 293774.490566 | 220263.453125 | 192404.141176 | 2.080533e+05 | 263562.442308 | 162707.047619 | 160023.058047 | 125572.947368 | 202458.870968 | 316928.093750 | 158633.333333 | 89500.000000 | 194377.650602 | 191664.592593 | 420882.857143 | 308849.624000 | 96667.804348 | 1.086933e+05 | 298563.664596 |
| 2004 | 329402.792453 | 278552.917647 | 164461.024691 | 2.344181e+05 | 289768.088496 | 194664.866667 | 192412.155612 | 153970.613757 | 221072.000000 | 324567.000000 | 123742.857143 | 106700.000000 | 219436.564593 | 282367.849315 | 446246.666667 | 363026.153153 | 130432.523810 | 1.290700e+05 | 342107.551020 |
| 2005 | 426188.960938 | 286630.454545 | 167020.134831 | 2.755594e+05 | 347167.045455 | 191624.068966 | 217297.821839 | 198988.218254 | 249984.560000 | 482439.934211 | 170333.333333 | 136355.555556 | 249690.024590 | 348068.765432 | 650704.166667 | 324567.159091 | 153823.477273 | 2.107688e+05 | 466759.200000 |
| 2006 | 479808.366071 | 326243.408451 | 198250.705882 | 2.962844e+05 | 365526.282051 | 157190.476190 | 228236.424437 | 315157.083019 | 346571.613757 | 499610.224599 | 187500.000000 | 138462.500000 | 272561.179104 | 328948.595745 | 940394.777778 | 845618.597938 | 612502.659574 | 1.144225e+06 | 543254.440000 |
| 2007 | 454656.623377 | 441204.406250 | 349346.316667 | 2.715699e+05 | 368487.084507 | 192095.428571 | 227287.531746 | 370066.811765 | 417076.501779 | 340181.672897 | 224211.000000 | 135250.000000 | 286244.118056 | 322284.523256 | 956710.600000 | 986335.367089 | 657535.476190 | 1.320538e+06 | 453832.204819 |
| 2008 | 424802.409091 | 434232.980392 | 372297.425532 | 2.546897e+05 | 345295.474576 | 211743.142857 | 219641.811159 | 186027.587786 | 431583.893491 | 293939.451923 | 221418.444444 | 146500.000000 | 275523.310606 | 306750.153061 | 480312.500000 | 395742.214286 | 152257.413793 | 1.428750e+05 | 417609.866667 |
| 2009 | 460578.097222 | 321978.756098 | 390018.485714 | 2.433003e+05 | 316922.368421 | 195049.000000 | 204029.867925 | 198929.500000 | 649794.384615 | 282908.325581 | 238329.111111 | 196000.000000 | 248864.745614 | 228124.065574 | 431433.500000 | 413963.019608 | 178393.750000 | 1.198790e+05 | 454004.113924 |
| 2010 | 497340.252525 | 351269.960784 | 358187.975000 | 2.317833e+05 | 315284.702703 | 164056.294118 | 188015.522727 | 227141.206731 | 848224.317073 | 268250.101449 | 258637.428571 | 214285.714286 | 237385.200000 | 199784.020833 | 413252.500000 | 396390.309524 | 187953.411765 | 8.571322e+04 | 490299.913043 |
| 2011 | 460955.980000 | 319574.968750 | 218509.476190 | 2.547875e+05 | 306011.689655 | 268002.913043 | 185890.656250 | 204060.103627 | 278248.473684 | 196566.695652 | 231000.000000 | 175946.666667 | 268075.053333 | 200193.740000 | 502326.315789 | 352747.850000 | 163675.296296 | 8.042573e+04 | 491891.779412 |
| 2012 | 420658.234694 | 313761.477612 | 221777.229167 | 2.554558e+05 | 308824.243902 | 284040.000000 | 201973.798419 | 170076.484472 | 259775.230769 | 197032.186813 | 266350.000000 | 122795.000000 | 278293.507614 | 212232.596491 | 534094.117647 | 386340.179104 | 142012.875000 | 1.044241e+05 | 433036.516854 |
| 2013 | 458520.567010 | 369592.786885 | 213980.024390 | 2.365795e+05 | 327563.000000 | 174065.166667 | 219900.988722 | 175690.820000 | 270205.818182 | 221882.442623 | 279620.000000 | 90500.000000 | 258260.692308 | 260129.777778 | 543196.466667 | 437618.711864 | 159847.185185 | 1.328353e+05 | 413494.467290 |
| 2014 | 481614.744186 | 369444.642857 | 277469.650000 | 2.626569e+05 | 339083.797468 | 192233.650000 | 239887.590164 | 186566.253623 | 308276.923077 | 248680.465839 | 273200.000000 | 161200.000000 | 277480.921296 | 245438.185714 | 605087.350000 | 389784.857143 | 176192.590909 | 1.934001e+05 | 411682.684685 |
| 2015 | 496539.625000 | 361467.213115 | 283404.280000 | 1.131422e+06 | 337120.517241 | 224104.545455 | 242761.353383 | 215265.649007 | 325096.000000 | 264166.722772 | 271650.000000 | 148100.000000 | 284277.179245 | 252323.639535 | 604076.190476 | 408567.148148 | 204481.285714 | 2.016584e+05 | 456843.147826 |
| 2016 | 703177.943396 | 431646.098765 | 275338.264151 | 9.857613e+05 | 363238.242105 | 219387.500000 | 264642.306050 | 225238.286550 | 328296.613636 | 280779.705556 | 403500.000000 | 135000.000000 | 321729.798354 | 301478.051948 | 612394.117647 | 551844.365079 | 239798.600000 | 1.863022e+05 | 529127.729167 |
| 2017 | 866624.000000 | 484094.019048 | 282198.044444 | 3.519668e+05 | 421612.043478 | 207086.363636 | 306167.406557 | 233705.700000 | 349164.152174 | 298332.752212 | 540000.000000 | 105000.000000 | 339523.180451 | 301986.506024 | 678141.846154 | 667565.425926 | 223282.031250 | 2.056954e+05 | 619536.436170 |
| 2018 | 793910.216867 | 498730.402062 | 299359.431818 | 3.616255e+05 | 445853.258427 | 217056.900000 | 315554.310345 | 241290.400000 | 361766.381818 | 293732.452381 | 750000.000000 | 105000.000000 | 320908.758065 | 323024.977778 | 746966.347826 | 654373.122449 | 224651.950000 | 1.982917e+05 | 626220.891089 |
| 2019 | 717857.860465 | 511822.775000 | 314556.120000 | 3.681375e+05 | 441875.581395 | 200313.800000 | 314220.600000 | 241635.603448 | 357772.259259 | 292720.743590 | 750000.000000 | 105000.000000 | 328184.814516 | 358465.073171 | 767669.529412 | 586843.750000 | 278134.142857 | 2.076667e+05 | 563986.363636 |
def plotyearlyregionaltrends():
avg, med = mean_med_byregion()
avg = avg.T
med = med.T
avg["Year"] = avg.index
med["Year"] = med.index
fig = ply.line(avg,x='Year',y = list(range(1,20)),labels='Regions')
fig.update_layout(
xaxis_title="Year",
yaxis_title="Average Sale Amount ($)")
fig.show()
fig.write_html("figures/plot-avg_sales_regional.html")
fig = ply.line(med,x='Year',y = list(range(1,20)))
fig.update_layout(
xaxis_title="Year",
yaxis_title="Median Sale Amount ($)")
fig.show()
fig.write_html("figures/plot-med_sales_regional.html")
return
plotyearlyregionaltrends()
Timeslider choropleth is similar to a regular choropleth in that both represent variables in regions using a gradient. However, the former can take in timestamps (time from a fixed date) and produce a draggable slider for time. Two helper functions are made to produce the timeslider map.
chooseColorTime(sale) is a helper function that is analogous to those in previous sections in that it will determine the color of each choropleth object based on the sales range.
makestyledict(sale) is a function that produces a style dictionary detailing the color of each region. It uses chooseColorTime(sale) as a helper function to determine the color. It first converts the date from a datetime format (MMM/DD/YYYY) to a unix integer that is the number of seconds since epoch (1/1/1970). It uses dictionary comprehension to make a nested dictionary. Outside keys are region numbers and inside keys are the time integer.
def chooseColorTime(sale):
if sale < 89000:
return '#fef0d9'
elif 89000<= sale < 150000:
return '#fdd49e'
elif 150000 <= sale < 300000:
return '#fdbb84'
elif 300000 <= sale < 500000:
return '#fc8d59'
elif 500000 <= sale < 800000:
return '#e34a33'
else:
return '#b30000'
def makestyledict():
datetime_index=pd.date_range(start='1/1/2000',end='1/1/2020',normalize=True,freq='A')
dt_index_epochs=datetime_index.astype(int)//10**9
dt_index=dt_index_epochs.astype(str)
style = {
str(outer_k): {
inner_k: 0
for inner_k in dt_index
}
for outer_k in np.arange(1,20)
}
avg_by_region.columns=dt_index
for region in style:
for time in style[region]:
saleavg = avg_by_region.at[int(region),str(time)]
style[region][time] = {'color': chooseColorTime(saleavg) ,'opacity':1}
return style
generateTimesliderMap() is a wrapper function that reads in the geojson file of all the regions, produces a map of Charlottesville, and then creates a Timeslider.
def generateTimesliderMap():
r = gpd.read_file('data/regions.geojson').set_index('OBJECTID')
m = folium.Map(location=city_cville_coord, zoom_start=13)
g = plugins.TimeSliderChoropleth(r.to_json(), styledict=makestyledict(), overlay = True).add_to(m)
m.save("figures/map-timeslider.html")
return m
generateTimesliderMap()
Another plugin produces a heat map that varies with time. The class takes in a list of list of coordinates, with the outer list being the time increment specified. The chosen time frame was in years, so the outer list has 20 lists corresponding to each of the past 20 years from 2000 to 2019. produceList() is a helper function that produces this nested list.
def produceList(df):
outer = []
for year in np.arange(2000,2020):
inner = []
temp = df[df['SaleDate'].dt.year==year].copy()
for house in temp.iterrows():
innerer = []
innerer.append(house[1].LATITUDE)
innerer.append(house[1].LONGITUDE)
innerer.append(house[1].SaleAmountScaled)
inner.append(innerer)
outer.append(inner)
return outer
def generateHeatMapSales():
df=pd.read_csv('data/salesLast20Years.csv')
df=df.drop(columns='Unnamed: 0')
df['SaleDate'] = pd.DatetimeIndex(df['SaleDate'])
dfLog = df.copy()
scaler = MinMaxScaler(feature_range=(0.0001,1))
df['SaleAmountScaled'] = scaler.fit_transform(df.loc[:,'SaleAmount'].values.reshape(-1,1))
scalerLog = MinMaxScaler(feature_range=(0.0001,1))
dfLog['SaleAmountScaled'] = scaler.fit_transform(np.log(df.loc[:,'SaleAmount'].values.reshape(-1,1)))
m = folium.Map(location=city_cville_coord, zoom_start=13)
data = produceList(df)
dataLog = produceList(dfLog)
yearsnorm = ['Norm: '+str(i) for i in np.arange(2000,2020)]
yearslog = ['Log: '+str(i) for i in np.arange(2000,2020)]
g = plugins.HeatMapWithTime(data=data,index=yearsnorm,name='Normal',
use_local_extrema=True,
min_speed = 1, speed_step = 1, max_speed = 5).add_to(m)
g = plugins.HeatMapWithTime(data = dataLog, index = yearslog, name = 'Log',
use_local_extrema=True,
min_speed = 1, speed_step = 1, max_speed = 5, show=False).add_to(m)
style_function = lambda x: {'fillOpacity': 0, 'opacity': 0.5}
folium.GeoJson("data/regions.geojson", style_function=style_function,name='Neighborhood Planning Areas').add_to(m)
folium.raster_layers.TileLayer('CartoDB Positron',show=True).add_to(m)
folium.raster_layers.TileLayer('Open Street Map',show=False).add_to(m)
folium.raster_layers.TileLayer('Stamen Toner',show=False).add_to(m)
folium.LayerControl().add_to(m)
m.save("figures/map-heatmap.html")
return m
generateHeatMapSales()
The section below observes which parcels have had multiple sales throughout the years. A quick check of the dimensions of the table show that there are quite a few parcels that have more than one sale in the records.
grpbysale = geoSalesResid.groupby(['ParcelNumber']).count().iloc[:,0]
multisales = grpbysale[grpbysale > 1]
print('Total sales: ' + str(geoSalesResid.shape[0]))
print('Unique parcels: ' + str(grpbysale.shape[0]))
print('Unique parcels with two or more sales: ' + str(multisales.shape[0]))
Total sales: 30618 Unique parcels: 12309 Unique parcels with two or more sales: 9107
A couple of functions are written to provide different ways of grouping the table and produce a sales history depending on conditions.
def normal(parcel):
temp = {}
if parcel.shape[0] == 1:
saledate = parcel.iloc[0,2]
temp[saledate] = parcel.iloc[0,3]
else:
for i in np.arange(parcel.shape[0]):
saledate = parcel.iloc[i,2]
temp[saledate] = parcel.iloc[i,3]
return temp
def byYear(parcel,year=2000):
temp = {}
if parcel.shape[0] == 1:
saledate = parcel.iloc[0,2]
if int(saledate[:4]) >= year:
temp[saledate] = parcel.iloc[0,3]
else:
for i in np.arange(parcel.shape[0]):
saledate = parcel.iloc[i,2]
if int(saledate[:4]) >= year:
temp[saledate] = parcel.iloc[i,3]
return temp
The getSalesHistory(table,norm=True,multi=True) function will return a dictionary of the sales in the table depending on the desired form of filtering (see above) list of functions. Multi parameter means only parcels with multiple sale histories are returned.
def getSalesHistory(table, norm=True, multi=True):
grouped = table.groupby('ParcelNumber')
if norm:
history = grouped.apply(normal).to_dict()
else:
temp = grouped.apply(byYear).to_dict()
history = {}
for parcel in temp:
if len(temp[parcel]) > 1:
history[parcel] = temp[parcel]
elif not multi and len(temp[parcel]) == 1:
history[parcel] = temp[parcel]
return history
Below, a sale history dictionary is created. Norm is set to false so that only sales occuring after the year 1999 are included. Multi is set to True so that only parcels with more than one sale as well are included in addition to the previous condition. In short, these two conditions mean that parcels in the dictionary will:
saleHistory = getSalesHistory(geoSalesResid,norm=False)
saleHistory
{'010017000': {'2017-06-08': 636000, '2018-10-19': 1030000},
'010017100': {'2000-09-14': 459500, '2005-11-10': 654750},
'010019000': {'2002-05-23': 85000,
'2006-07-07': 479500,
'2003-03-03': 360000,
'2001-11-01': 70000,
'2008-08-07': 549000},
'010020000': {'2010-08-04': 962500, '2001-01-24': 275000},
'010024A00': {'2002-06-12': 125000,
'2014-06-30': 940000,
'2007-08-29': 895000},
'010027000': {'2008-08-04': 483000, '2017-12-08': 695000},
'010031000': {'2003-06-06': 425000, '2008-12-15': 545000},
'010034000': {'2018-11-15': 430000, '2019-02-15': 470000},
'010036000': {'2014-03-21': 695940, '2010-11-01': 389000},
'010037000': {'2016-05-05': 740000, '2001-07-02': 345000},
'010038000': {'2018-04-09': 685000, '2019-03-29': 1375000},
'010039000': {'2007-07-13': 520000, '2007-05-25': 520000},
'010041000': {'2004-03-03': 530000, '2019-06-06': 1350000},
'010043000': {'2020-01-07': 680000,
'2018-07-17': 660000,
'2011-05-25': 315000,
'2015-05-06': 590000},
'010045000': {'2008-07-30': 745000, '2006-11-29': 725000},
'020003000': {'2007-08-10': 948000, '2003-06-03': 500000},
'020004000': {'2005-05-06': 377500, '2004-02-11': 275000},
'020006000': {'2009-04-23': 600000, '2003-11-18': 310000},
'020006100': {'2016-10-05': 355000,
'2007-04-17': 362500,
'2004-05-03': 300000},
'020006300': {'2012-11-29': 386375, '2015-06-30': 412500},
'020010000': {'2005-03-02': 430000,
'2016-12-20': 155000,
'2019-08-19': 373450},
'020012000': {'2005-12-14': 350000,
'2006-06-12': 350000,
'2016-07-28': 379000},
'020015000': {'2019-01-25': 252500, '2019-10-17': 550000},
'020016100': {'2010-06-03': 350000, '2016-06-30': 478000},
'020016200': {'2015-01-28': 415000, '2013-10-16': 83000},
'020017000': {'2000-02-29': 208000, '2001-08-31': 339000},
'020019000': {'2020-05-13': 1975000, '2003-06-13': 810000},
'020019100': {'2011-12-01': 640000, '2018-08-10': 767500},
'020023A00': {'2002-06-10': 2967,
'2016-10-07': 851000,
'2017-02-23': 848000,
'2013-02-12': 650000},
'020025000': {'2010-09-02': 1100000, '2015-07-27': 735000},
'020028000': {'2010-12-29': 675000, '2002-07-23': 385000},
'020029000': {'2002-03-04': 425000,
'2002-05-28': 456000,
'2015-06-12': 776795},
'020042000': {'2018-04-02': 593800,
'2010-07-09': 550000,
'2020-04-24': 814000},
'020044000': {'2008-06-16': 425000, '2006-05-01': 408400},
'020046000': {'2002-08-09': 402000, '2000-12-11': 264000},
'020051000': {'2015-04-21': 515000, '2011-01-18': 418000},
'020054200': {'2010-07-13': 650000, '2003-10-31': 525000},
'020054500': {'2004-08-23': 20963,
'2013-05-24': 960000,
'2009-05-14': 210000},
'020055200': {'2011-06-27': 500000, '2013-09-06': 750000},
'020055500': {'2011-04-22': 560000, '2017-08-04': 631250},
'020055600': {'2003-06-16': 405000, '2000-08-02': 265400},
'020057000': {'2007-06-01': 395000,
'2018-06-08': 785000,
'2013-09-09': 720000,
'2002-07-16': 429000},
'020058000': {'2008-02-01': 191350, '2015-03-24': 375000},
'020061000': {'2015-01-20': 850000,
'2000-04-19': 335000,
'2012-07-23': 753000},
'020062000': {'2007-02-27': 515000, '2014-11-12': 549000},
'020066000': {'2015-06-09': 495000, '2002-11-15': 309000},
'020070000': {'2008-11-21': 44900, '2004-08-20': 1100000},
'020072000': {'2016-02-02': 695600, '2020-04-29': 990000},
'030001000': {'2010-09-15': 465000, '2012-06-22': 480000},
'030004000': {'2002-12-10': 213000,
'2019-03-05': 735000,
'2008-07-03': 435500,
'2000-04-28': 153000},
'030010000': {'2013-07-10': 450000,
'2009-10-28': 399900,
'2018-06-05': 595000,
'2007-08-13': 370000},
'030015000': {'2005-07-20': 380000,
'2013-05-29': 427000,
'2016-08-17': 460000,
'2003-09-08': 350000,
'2001-02-07': 225000},
'030016000': {'2005-07-27': 425000, '2015-07-09': 739000},
'030023000': {'2003-03-24': 258000,
'2017-12-01': 520000,
'2001-08-07': 175000},
'030042000': {'2016-08-26': 459000,
'2007-06-29': 420000,
'2003-07-22': 266000},
'030043000': {'2019-07-02': 653000, '2001-07-02': 245000},
'030045000': {'2006-02-28': 799000, '2017-06-01': 769000},
'030045100': {'2004-09-15': 238000, '2009-12-17': 285000},
'030046000': {'2012-04-30': 500000, '2003-07-02': 455000},
'030048000': {'2004-06-07': 309000, '2013-06-04': 341000},
'030050000': {'2005-07-28': 380000, '2014-05-27': 349900},
'030051000': {'2014-02-21': 417438,
'2018-06-19': 465000,
'2015-02-06': 290000,
'2006-08-02': 379500},
'030052000': {'2004-07-27': 540000, '2016-12-09': 630000},
'030052100': {'2002-08-15': 325000, '2006-05-05': 390000},
'030059000': {'2008-07-23': 122744,
'2004-01-09': 160000,
'2008-08-15': 126000},
'030060100': {'2001-03-13': 246000, '2005-05-24': 375000},
'030060101': {'2005-06-24': 410000, '2011-08-05': 340000},
'030060120': {'2010-04-15': 620000, '2001-03-23': 50000},
'030060130': {'2018-07-26': 543510, '2000-01-14': 211963},
'030060140': {'2002-02-13': 10000, '2007-05-25': 585000},
'030060160': {'2000-11-17': 276935, '2011-06-13': 498000},
'030060170': {'2013-04-11': 449000,
'2008-05-01': 447500,
'2000-07-13': 244900},
'030060200': {'2004-07-16': 370000,
'2000-09-19': 240000,
'2013-08-16': 401000},
'030060300': {'2003-05-29': 302000, '2019-10-10': 503000},
'030060400': {'2000-07-12': 208825, '2009-09-01': 415500},
'030060600': {'2005-03-02': 620000,
'2004-01-07': 190000,
'2015-05-20': 684500},
'030060700': {'2004-01-07': 190000,
'2018-05-01': 625000,
'2006-06-01': 807794},
'030060800': {'2001-06-13': 283500, '2004-06-22': 365000},
'030060900': {'2005-10-18': 412000,
'2002-07-31': 295000,
'2012-09-12': 330000},
'030065000': {'2005-07-27': 10000, '2004-08-03': 310000},
'030067000': {'2004-10-27': 38000, '2002-03-22': 140000},
'030067100': {'2002-06-25': 193000,
'2003-06-04': 215000,
'2008-06-17': 349000},
'030073000': {'2005-10-20': 327500, '2001-04-06': 126000},
'030076000': {'2000-12-08': 30000, '2006-10-23': 375000},
'030077000': {'2006-10-23': 375000,
'2004-04-29': 65000,
'2001-05-14': 36000,
'2004-10-05': 91500},
'030079000': {'2014-12-19': 125000, '2015-07-13': 448000},
'030080101': {'2005-03-24': 320000, '2004-08-12': 289900},
'030080200': {'2003-07-01': 385000, '2005-08-25': 428000},
'030080300': {'2005-07-22': 325000, '2020-05-01': 445000},
'030080400': {'2015-08-28': 385000,
'2003-07-18': 291000,
'2013-01-23': 375000},
'030080600': {'2007-07-31': 415000,
'2000-07-24': 178000,
'2003-09-16': 266000},
'030080700': {'2003-08-26': 278000,
'2016-03-04': 315000,
'2006-04-04': 349000},
'030080800': {'2001-06-28': 205000,
'2005-02-10': 350000,
'2016-06-09': 421500},
'030089000': {'2005-06-16': 325000, '2020-03-05': 375000},
'030090000': {'2010-11-23': 150401,
'2011-03-08': 163000,
'2006-07-12': 155000},
'030097000': {'2002-12-17': 166500,
'2002-05-15': 107000,
'2019-12-11': 275000},
'030098000': {'2000-06-20': 15430, '2003-09-05': 62000},
'030100000': {'2002-10-16': 5772, '2002-02-20': 4160},
'030108000': {'2004-04-01': 90000, '2000-12-07': 53000},
'030109000': {'2006-05-25': 180000,
'2015-03-09': 220000,
'2002-09-18': 70000,
'2020-05-07': 304000,
'2004-05-11': 119500},
'030110000': {'2005-07-27': 175000, '2010-09-16': 135000},
'030113000': {'2017-10-31': 195000,
'2012-08-07': 125000,
'2012-11-30': 141900},
'030140000': {'2006-06-06': 245000, '2001-08-02': 128000},
'030142000': {'2002-05-02': 140000,
'2007-05-02': 250000,
'2018-07-23': 231000},
'040008000': {'2017-10-11': 215000,
'2007-07-12': 173000,
'2006-06-22': 7795,
'2006-04-14': 130000,
'2002-06-04': 64000},
'040020000': {'2008-05-23': 215000,
'2012-03-12': 195000,
'2007-09-13': 132282},
'040033000': {'2009-07-07': 71000, '2005-04-04': 2500, '2009-02-20': 159814},
'040047000': {'2006-07-11': 85000, '2007-06-19': 355000},
'040067000': {'2014-05-23': 386000, '2013-07-17': 63000},
'040070000': {'2004-08-09': 245000, '2002-05-09': 70000},
'040076000': {'2015-02-18': 50000,
'2012-03-08': 140000,
'2016-11-07': 170000},
'040083000': {'2019-10-02': 502000,
'2016-10-31': 400000,
'2016-03-29': 179845},
'040086000': {'2007-01-23': 245000, '2017-01-10': 265500},
'040099000': {'2015-03-23': 125000,
'2018-12-17': 235000,
'2020-02-28': 416000},
'040100000': {'2013-12-19': 231000,
'2012-07-28': 398803,
'2013-07-03': 398809,
'2004-02-25': 40800,
'2004-12-06': 171940},
'040110000': {'2000-11-17': 19000, '2013-10-15': 57000},
'040136000': {'2000-02-18': 85000, '2017-05-18': 265000},
'040137000': {'2008-07-02': 180000, '2015-11-16': 222000},
'040137100': {'2019-08-14': 280000,
'2015-08-10': 240000,
'2008-06-26': 180000},
'040138000': {'2005-10-27': 238200, '2002-11-18': 70000, '2004-02-03': 87000},
'040140000': {'2001-02-26': 61000, '2000-11-17': 56000},
'040147000': {'2005-06-16': 220000,
'2004-06-07': 11000,
'2008-07-28': 286000},
'040151000': {'2012-05-25': 125000, '2015-11-13': 176750},
'040169000': {'2003-02-11': 35000,
'2002-10-16': 600,
'2002-03-11': 7000,
'2004-10-04': 145750},
'040171000': {'2004-01-20': 120000, '2005-12-30': 172500},
'040174000': {'2005-01-06': 105000, '2005-07-13': 174900},
'040181000': {'2017-05-30': 172500,
'2008-07-02': 139500,
'2007-10-11': 90000},
'040182000': {'2012-06-18': 114750,
'2013-01-17': 111100,
'2003-06-19': 55000},
'040184000': {'2016-07-06': 330900,
'2013-03-13': 144000,
'2013-07-31': 66299,
'2003-06-19': 45000},
'040194000': {'2001-02-13': 17000, '2002-01-31': 79000},
'040195000': {'2017-01-17': 90000, '2005-01-12': 46533},
'040197000': {'2005-02-07': 155000,
'2004-01-06': 39359,
'2004-03-30': 58500,
'2004-10-12': 90000,
'2017-08-03': 248000},
'040205000': {'2002-02-01': 100000, '2000-10-24': 45000},
'040211000': {'2014-06-12': 215000, '2008-09-05': 150000},
'040212000': {'2008-09-04': 122500,
'2013-03-29': 194000,
'2016-11-22': 289000},
'040213000': {'2018-11-26': 311600, '2018-05-16': 151000},
'040219000': {'2015-12-17': 117000,
'2017-09-20': 215000,
'2019-03-22': 254939},
'040221000': {'2000-02-29': 45000,
'2007-04-02': 121025,
'2016-04-11': 250000},
'040225000': {'2020-01-22': 330000, '2006-04-25': 219900, '2000-05-26': 5500},
'040227000': {'2007-01-04': 250000, '2001-01-19': 286400},
'040228000': {'2000-08-14': 55750, '2006-06-19': 345000},
'040229000': {'2002-01-31': 30500,
'2006-04-20': 305000,
'2008-07-16': 299000,
'2019-11-12': 369000},
'040231000': {'2001-01-19': 286400,
'2017-06-01': 365000,
'2006-03-20': 300000},
'040232000': {'2005-12-08': 299900,
'2013-05-29': 286000,
'2001-01-19': 286400},
'040242000': {'2011-12-13': 70000, '2011-04-08': 63000, '2011-03-17': 100643},
'040242100': {'2003-06-30': 25000, '2005-09-13': 152000},
'040265000': {'2005-02-23': 184000,
'2008-05-27': 252500,
'2001-01-19': 286400,
'2016-06-07': 315000},
'040269000': {'2005-08-29': 225000,
'2003-06-26': 51000,
'2009-12-29': 185000},
'040270000': {'2016-12-19': 188500,
'2017-04-26': 335000,
'2001-01-19': 286400,
'2003-09-02': 125000},
'040271000': {'2003-07-30': 127950,
'2013-12-09': 225000,
'2010-10-18': 180000,
'2001-01-19': 286400},
'0402771D0': {'2006-01-12': 228000,
'2012-10-10': 226000,
'2004-12-23': 193500},
'0402772B0': {'2017-04-24': 285000, '2009-07-16': 222500},
'0402772G0': {'2009-07-28': 222500, '2017-05-08': 282000},
'040280100': {'2004-02-06': 775000,
'2001-12-05': 38000,
'2007-04-20': 2400000},
'040280101': {'2012-06-29': 145600,
'2005-02-25': 115000,
'2004-11-01': 81500},
'040280120': {'2002-08-29': 62000, '2007-04-20': 2400000},
'040280140': {'2004-06-15': 78000,
'2005-03-01': 131100,
'2009-08-05': 140000},
'040280150': {'2007-04-20': 2400000,
'2003-10-31': 57000,
'2004-02-06': 775000},
'040280160': {'2002-05-16': 44500,
'2007-04-20': 2400000,
'2004-02-06': 775000},
'040280170': {'2007-03-27': 156500,
'2015-07-31': 156000,
'2003-06-05': 88500,
'2002-05-31': 62500},
'040280190': {'2002-01-28': 47000,
'2007-04-20': 2400000,
'2004-02-06': 775000},
'040280200': {'2004-02-06': 775000,
'2002-02-01': 44500,
'2007-04-20': 2400000},
'040280210': {'2005-01-19': 77000,
'2007-04-20': 2400000,
'2001-02-23': 44000},
'040280220': {'2004-01-20': 88000, '2010-04-23': 153000},
'040280230': {'2007-07-02': 155000,
'2004-02-06': 92000,
'2017-03-31': 178000,
'2002-09-19': 53000},
'040280240': {'2002-06-03': 49900,
'2007-04-20': 2400000,
'2004-02-06': 775000},
'040280250': {'2004-02-06': 775000,
'2007-04-20': 2400000,
'2002-02-28': 31500},
'040280260': {'2002-08-29': 248000, '2007-04-20': 2400000},
'040280290': {'2004-02-06': 775000,
'2003-10-31': 55000,
'2007-04-20': 2400000},
'040280300': {'2002-08-29': 248000, '2007-04-20': 2400000},
'040280301': {'2007-04-20': 2400000, '2002-08-29': 248000},
'040280310': {'2002-06-03': 49000,
'2004-02-06': 775000,
'2007-04-20': 2400000},
'040280330': {'2007-04-20': 2400000,
'2003-10-14': 45000,
'2004-02-06': 775000},
'040280340': {'2007-04-20': 2400000, '2002-08-29': 248000},
'040280360': {'2011-09-01': 140000, '2002-12-12': 63000},
'040280400': {'2002-05-16': 31000,
'2004-02-06': 775000,
'2007-04-20': 2400000},
'040280600': {'2007-04-20': 2400000,
'2004-02-06': 775000,
'2002-09-17': 51000},
'040280900': {'2005-06-01': 128000,
'2003-04-15': 85500,
'2014-07-31': 144500},
'040303101': {'2012-04-25': 125200, '2015-07-17': 138333},
'040303110': {'2015-04-03': 99000, '2003-05-30': 76500, '2011-09-06': 110000},
'040303120': {'2007-04-03': 160000,
'2001-06-04': 52500,
'2018-03-15': 530000},
'040303130': {'2003-03-11': 70000,
'2018-03-15': 530000,
'2007-04-03': 160000},
'040303170': {'2005-01-18': 137500, '2015-01-30': 165000},
'040303180': {'2019-05-22': 175000,
'2015-09-10': 140000,
'2014-09-23': 267000},
'040303190': {'2003-10-30': 107000, '2012-03-07': 122000},
'040303310': {'2004-12-13': 118000,
'2003-06-26': 104000,
'2010-09-02': 122000},
'040303340': {'2012-08-30': 122600,
'2015-07-17': 138333,
'2000-03-10': 50000},
'040303370': {'2004-03-03': 110000,
'2006-04-04': 170000,
'2003-04-11': 99500,
'2017-11-15': 168000},
'040303390': {'2003-04-16': 93000, '2009-12-09': 155000, '2003-01-07': 56000},
'040303400': {'2018-03-15': 530000, '2008-06-04': 142000},
'040303401': {'2006-06-13': 165000,
'2015-07-17': 138333,
'2014-10-09': 145000},
'040303430': {'2016-08-12': 160000,
'2009-06-16': 120000,
'2012-05-25': 132000},
'040303700': {'2014-09-23': 267000,
'2015-09-10': 142500,
'2017-02-21': 149000},
'040304100': {'2010-08-19': 135000, '2000-09-27': 54000},
'040304150': {'2005-03-30': 117000, '2002-12-12': 67000},
'040304160': {'2000-01-31': 44000, '2002-06-11': 60000},
'040304200': {'2001-08-24': 51000, '2018-11-19': 158000},
'040304210': {'2001-01-08': 51250, '2005-01-06': 110000},
'040304220': {'2009-09-30': 129000, '2002-01-31': 51500},
'040304230': {'2012-04-06': 132500,
'2000-05-19': 50500,
'2013-12-20': 137500,
'2003-07-01': 85000},
'040304240': {'2003-08-01': 82500, '2001-04-30': 55000},
'040304250': {'2000-06-30': 50000, '2006-03-10': 136000},
'040304290': {'2005-02-01': 100000,
'2001-07-19': 53250,
'2011-10-27': 118500},
'040304301': {'2003-01-27': 75000, '2001-05-02': 52500},
'040304310': {'2004-11-23': 105500,
'2008-05-20': 139900,
'2013-06-04': 134000,
'2000-07-26': 49500,
'2015-08-05': 140000},
'040304320': {'2007-04-18': 136000, '2000-01-31': 44000},
'040304340': {'2005-11-07': 88000, '2015-02-26': 140000, '2000-01-31': 44000},
'040304350': {'2006-06-19': 159000,
'2015-03-02': 150000,
'2000-08-04': 53500},
'040304360': {'2004-09-27': 112000,
'2003-01-21': 67000,
'2015-08-05': 140000,
'2012-02-22': 135000},
'040304370': {'2000-09-20': 51000,
'2010-06-01': 135000,
'2006-01-18': 115000},
'040304380': {'2009-06-04': 134000, '2000-11-14': 56000},
'040304410': {'2003-04-30': 76000, '2001-02-06': 53500, '2015-08-05': 137000},
'040304500': {'2005-05-02': 115000, '2002-12-12': 73200},
'040304900': {'2011-05-31': 126250, '2003-05-23': 78000},
'040318000': {'2006-07-28': 224900,
'2002-01-16': 36000,
'2014-05-20': 229000,
'2016-07-29': 265000,
'2011-09-09': 210000},
'040319000': {'2012-04-12': 211500,
'2006-07-17': 224900,
'2002-01-24': 40000},
'040320000': {'2006-04-14': 224900,
'2001-12-21': 42000,
'2018-02-26': 270000},
'050001000': {'2002-12-02': 250000,
'2011-09-27': 500000,
'2009-12-10': 370000,
'2016-04-11': 525000},
'050009000': {'2004-08-02': 460000, '2014-08-14': 510000},
'050012000': {'2000-10-26': 320000,
'2005-08-01': 650000,
'2003-07-30': 425000},
'050015L00': {'2015-04-14': 699000, '2013-11-04': 500000},
'050022000': {'2007-09-19': 470000, '2003-06-19': 400000, '2016-10-04': 4000},
'050024100': {'2008-06-24': 882500, '2004-01-20': 534000},
'050025000': {'2005-07-18': 375000, '2005-12-29': 445000},
'050031000': {'2004-03-19': 742500, '2012-02-07': 1250000},
'050032000': {'2002-05-31': 605000, '2016-04-29': 875000},
'050100100': {'2006-06-06': 185000,
'2002-08-09': 96900,
'2005-06-02': 155000},
'050100200': {'2004-06-03': 125000,
'2013-11-04': 187000,
'2001-06-18': 89900,
'2015-12-22': 190000,
'2008-02-22': 200000},
'050100300': {'2003-01-15': 102000,
'2004-03-05': 127500,
'2019-07-01': 235000},
'050100400': {'2016-03-04': 190000,
'2006-05-22': 174000,
'2003-06-05': 106500,
'2000-02-07': 74000},
'050100500': {'2013-05-31': 195000, '2009-08-05': 184900},
'050100600': {'2006-06-02': 188000, '2003-06-02': 107000},
'050100700': {'2008-06-17': 175000,
'2019-01-17': 210000,
'2002-06-27': 96900,
'2014-01-07': 180000},
'050100900': {'2020-06-16': 248000,
'2006-05-10': 200000,
'2002-09-27': 94900,
'2005-05-27': 150000,
'2012-11-30': 200000},
'050118000': {'2015-10-05': 690000, '2018-11-20': 1150000},
'050119000': {'2002-09-27': 514100, '2001-06-15': 465000},
'050122000': {'2004-03-29': 375000, '2000-12-18': 285000},
'050133000': {'2016-07-05': 1125000, '2013-07-09': 1050000},
'050140000': {'2007-12-17': 375000, '2010-12-17': 270000},
'050141300': {'2012-11-20': 320000,
'2001-06-12': 175000,
'2004-02-17': 232000,
'2007-09-19': 260000},
'050141400': {'2017-03-31': 279500, '2005-06-15': 250000},
'050143000': {'2006-08-14': 425000, '2004-01-28': 255000},
'050147100': {'2002-10-24': 125000,
'2003-10-27': 230000,
'2017-06-22': 1647000},
'060001801': {'2003-01-27': 80000, '2001-06-15': 75000},
'060001802': {'2009-07-10': 135000, '2012-12-14': 172000},
'060001811': {'2011-08-02': 250000, '2006-05-31': 110000},
'060001813': {'2008-10-15': 158000, '2006-04-18': 175000},
'060001818': {'2000-07-31': 45500, '2013-06-17': 90500},
'060001827': {'2005-09-02': 132000, '2011-08-02': 250000},
'060001831': {'2006-01-17': 166700, '2016-04-15': 165000},
'060001833': {'2010-08-12': 130000,
'2003-06-30': 78000,
'2015-12-01': 155500},
'060001834': {'2010-06-07': 300000, '2004-07-30': 118500},
'060001836': {'2000-02-04': 49000,
'2018-04-16': 105000,
'2006-04-21': 110500},
'060001837': {'2010-03-26': 115000, '2011-02-03': 155000},
'060001844': {'2010-06-07': 300000, '2000-08-31': 79900},
'060001846': {'2000-11-20': 67500, '2015-08-07': 166900},
'060001847': {'2006-01-05': 105000, '2000-12-01': 39250},
'060004000': {'2008-06-24': 730000, '2004-03-01': 450000},
'060005000': {'2007-05-02': 790000,
'2003-05-28': 517000,
'2004-07-30': 668000},
'060010000': {'2016-06-30': 574000,
'2018-01-31': 1451733,
'2016-08-31': 460750},
'060011000': {'2000-01-12': 302000,
'2007-06-01': 762000,
'2005-06-30': 710000},
'060014000': {'2004-05-20': 415000,
'2006-05-23': 515000,
'2009-08-17': 757500},
'060015100': {'2009-09-16': 570000, '2016-01-14': 650000},
'060016000': {'2010-10-27': 504500,
'2009-06-26': 490000,
'2008-12-01': 900000},
'060018000': {'2016-04-25': 975000,
'2009-11-19': 750000,
'2020-04-22': 1185000},
'060022000': {'2005-07-01': 545000,
'2001-02-14': 305000,
'2003-09-10': 300000,
'2004-01-05': 365000},
'060024000': {'2001-08-20': 475000, '2004-05-26': 715000},
'060024A00': {'2005-10-24': 959000,
'2002-08-15': 660000,
'2001-11-16': 125000},
'060026000': {'2006-06-15': 570000,
'2017-09-14': 1315000,
'2010-06-01': 1050000},
'060027000': {'2009-10-19': 1050000, '2019-05-16': 1390000},
'060032000': {'2006-04-27': 455000, '2000-12-01': 389000},
'060035000': {'2018-08-07': 1140000,
'2007-07-27': 718000,
'2015-09-23': 890000},
'060042000': {'2013-02-15': 945000,
'2004-03-26': 625000,
'2001-01-17': 400000,
'2009-07-16': 875000},
'060044100': {'2001-06-15': 410000,
'2009-05-14': 850000,
'2004-02-19': 557500},
'060049000': {'2002-06-14': 300000, '2017-05-31': 542500},
'060056000': {'2011-01-31': 820000, '2003-11-20': 635000},
'060057000': {'2006-03-28': 739000, '2012-06-28': 706000},
'060058000': {'2012-05-09': 745000, '2011-09-30': 390000},
'060061000': {'2009-07-01': 430000, '2019-06-19': 1000000},
'060062000': {'2001-02-09': 345000,
'2014-01-21': 650000,
'2017-05-23': 960000,
'2019-04-17': 900000},
'060066000': {'2017-06-05': 798250, '2013-06-04': 580000},
'060067000': {'2018-08-02': 1550000,
'2004-06-15': 754000,
'2015-08-28': 1600000},
'060069000': {'2014-06-19': 841500, '2012-12-12': 1275000},
'060070000': {'2002-05-29': 500000, '2000-03-27': 490000},
'060080000': {'2016-08-30': 1020000, '2005-06-10': 530000},
'060085100': {'2005-05-05': 315000,
'2009-12-01': 415000,
'2013-09-19': 450000},
'060085300': {'2013-09-03': 499000, '2010-05-25': 375000},
'060085400': {'2011-01-06': 355000, '2014-06-25': 450000},
'060085500': {'2019-08-02': 520000,
'2016-12-02': 505000,
'2006-04-14': 365000},
'060085600': {'2019-10-18': 520000,
'2005-08-29': 387500,
'2007-12-18': 452500},
'060089000': {'2010-12-22': 495000, '2015-09-28': 520000},
'060090000': {'2005-08-15': 712000,
'2003-06-16': 495000,
'2014-08-22': 925000},
'060091000': {'2000-08-16': 325000, '2005-12-22': 524500},
'070003110': {'2012-06-07': 590000,
'2004-04-20': 440000,
'2011-07-29': 521700},
'070003120': {'2011-07-29': 150000,
'2011-06-13': 150000,
'2004-04-20': 180000},
'070003150': {'2014-10-22': 523347,
'2000-01-14': 197900,
'2003-08-29': 335000},
'070003900': {'2004-07-30': 550000,
'2016-11-04': 540000,
'2007-04-25': 525000},
'070004000': {'2009-06-26': 510000,
'2006-01-12': 187500,
'2000-06-30': 196500},
'070006000': {'2008-09-17': 449000, '2000-07-18': 250000},
'070007000': {'2013-07-02': 540000, '2001-08-01': 300000},
'070008000': {'2010-06-02': 136834, '2016-09-02': 532800},
'070009000': {'2001-11-09': 425000,
'2007-07-11': 627000,
'2012-06-29': 600000},
'070012000': {'2010-05-14': 667500,
'2002-12-03': 525000,
'2017-04-04': 750000},
'070013000': {'2008-06-27': 520000,
'2000-05-26': 207000,
'2005-04-04': 490000,
'2004-02-26': 373000,
'2012-10-24': 440000},
'070014000': {'2006-06-15': 493000, '2004-09-28': 330000},
'070019000': {'2005-10-31': 469000, '2017-07-10': 555000},
'070024000': {'2012-10-16': 840000, '2003-09-18': 670000},
'070030000': {'2000-09-12': 182740, '2001-06-15': 350000},
'070033000': {'2007-10-10': 965000, '2000-04-28': 280000},
'070034100': {'2003-10-31': 575000, '2019-10-11': 731382},
'070040000': {'2015-08-05': 482800,
'2010-04-15': 560000,
'2018-12-19': 550000},
'070047000': {'2019-10-01': 733000, '2012-04-03': 250000},
'070048000': {'2012-06-19': 475000, '2005-02-22': 480000},
'080015000': {'2011-12-15': 395000,
'2006-01-25': 435000,
'2010-08-16': 280501},
'080016000': {'2000-07-17': 243900, '2005-12-29': 672000},
'080017000': {'2005-08-23': 564900, '2015-10-19': 848000},
'080040000': {'2006-01-18': 3000000,
'2007-07-25': 4687106,
'2003-12-16': 800000},
'080051000': {'2011-06-09': 620000, '2005-07-18': 659000},
'080052000': {'2002-08-28': 505000, '2012-06-12': 735000},
'080053000': {'2001-01-04': 350000, '2004-06-09': 486000},
'080057000': {'2002-05-28': 80000, '2003-09-10': 455000},
'080060000': {'2003-10-17': 640000,
'2014-05-16': 1050000,
'2003-08-01': 640000},
'090021000': {'2018-07-31': 665000, '2016-06-02': 600000},
'090050000': {'2000-08-09': 200000, '2001-06-19': 250000},
'090116000': {'2014-09-16': 265000, '2005-09-08': 225000},
'090117000': {'2013-07-30': 249000, '2005-09-01': 220000},
'110008000': {'2017-02-01': 675000, '2003-05-09': 55000},
'110041000': {'2007-08-22': 306000,
'2001-05-01': 153000,
'2016-06-24': 332500,
'2004-06-21': 236640},
'110042000': {'2013-03-26': 301000, '2006-02-28': 200000},
'110043000': {'2013-08-29': 345000,
'2015-10-30': 361200,
'2005-06-01': 374000,
'2001-09-07': 175000},
'110050000': {'2005-05-27': 480000, '2014-04-16': 425000},
'110053100': {'2005-04-18': 553909,
'2008-06-05': 685000,
'2002-05-01': 305000,
'2018-07-03': 780000},
'110061100': {'2005-09-22': 255000,
'2018-09-18': 250000,
'2014-02-03': 230000},
'110066000': {'2015-03-17': 290000, '2005-05-02': 259000},
'110067000': {'2004-09-30': 279000, '2015-03-17': 310000},
'110070000': {'2003-10-31': 375000,
'2006-09-20': 480000,
'2018-07-20': 538000},
'110079000': {'2004-01-07': 260000, '2016-12-14': 412185},
'110102400': {'2000-02-01': 97000, '2016-12-16': 550000},
'110102500': {'2006-05-09': 439000, '2017-01-17': 1050000},
'110102600': {'2017-01-17': 1050000, '2006-05-09': 439000},
'110102700': {'2006-05-09': 439000, '2017-01-17': 1050000},
'110117212': {'2008-01-22': 239999, '2011-04-27': 175000},
'110117240': {'2009-06-15': 344500, '2007-12-12': 284182},
'110117260': {'2016-06-10': 269000,
'2012-05-16': 257000,
'2007-09-07': 299000},
'110117280': {'2007-08-29': 299000, '2016-07-20': 268000},
'110117290': {'2014-02-26': 360000, '2007-11-02': 375000},
'110117340': {'2013-05-02': 380000, '2007-09-24': 448500},
'110117370': {'2007-08-31': 299000, '2011-02-15': 260000},
'110117380': {'2007-09-19': 475000, '2015-04-17': 420000},
'110117390': {'2007-09-27': 375000,
'2012-03-19': 320000,
'2015-12-18': 299900},
'110117480': {'2007-10-26': 299000, '2018-01-18': 293000},
'110117510': {'2008-01-25': 619125,
'2016-12-19': 448000,
'2010-01-29': 450000,
'2013-06-14': 600000},
'110117530': {'2007-09-27': 575000, '2018-04-10': 443500},
'110117560': {'2017-02-24': 420000,
'2008-12-08': 579500,
'2019-10-11': 465000},
'110117580': {'2008-09-30': 579500, '2017-02-13': 425000},
'110117700': {'2018-05-24': 270000, '2008-03-20': 299000},
'110117800': {'2012-08-02': 250000,
'2014-07-01': 250000,
'2007-09-17': 299000,
'2020-06-01': 280000},
'110117900': {'2013-09-19': 361000,
'2007-09-05': 375000,
'2018-08-27': 329000},
'120005000': {'2019-04-30': 750000, '2019-12-05': 890000},
'120009000': {'2016-05-24': 371000, '2006-11-14': 300000},
'120029000': {'2019-03-22': 1000000, '2017-05-16': 899000},
'130001000': {'2017-07-28': 770000, '2003-02-04': 485000},
'130002000': {'2002-03-28': 420000,
'2018-03-02': 705000,
'2006-02-03': 595000},
'130003000': {'2012-04-20': 455000, '2003-04-30': 372000},
'130004000': {'2002-06-17': 405000,
'2019-04-18': 1270000,
'2017-04-10': 695000},
'130011000': {'2004-08-12': 435000,
'2016-09-19': 630000,
'2019-03-22': 1300000},
'130015000': {'2014-04-22': 689500,
'2006-10-20': 702500,
'2002-04-30': 508000},
'130021000': {'2004-01-23': 367000,
'2016-06-02': 540000,
'2007-12-03': 545000},
'130030000': {'2003-06-27': 430000, '2008-01-30': 620000},
'130036000': {'2003-05-02': 420000,
'2018-05-14': 692312,
'2009-01-29': 400000},
'130038000': {'2019-12-06': 537500, '2001-10-01': 401200},
'130045200': {'2014-11-17': 422000, '2001-01-31': 219950},
'130047000': {'2000-09-20': 230000, '2020-02-24': 625000},
'130050000': {'2001-03-28': 250000, '2005-09-30': 464000},
'130059000': {'2005-10-26': 295000,
'2016-06-15': 625000,
'2004-02-13': 12500},
'130060000': {'2009-06-01': 562500, '2019-10-16': 575000},
'130061000': {'2019-09-26': 660000, '2012-09-18': 610000},
'130064000': {'2005-09-30': 487000, '2002-08-08': 389000},
'130070000': {'2006-11-21': 435000,
'2002-07-08': 265000,
'2014-07-03': 491000},
'130072000': {'2016-07-27': 614500,
'2013-09-04': 525000,
'2005-09-02': 525000,
'2011-01-04': 525000},
'130072100': {'2013-10-01': 555100, '2004-06-03': 439000},
'130078000': {'2001-11-16': 515000,
'2005-08-08': 809000,
'2006-11-22': 935000},
'160025000': {'2004-04-09': 329000, '2018-02-05': 460000},
'160032000': {'2019-10-04': 695000, '2012-03-30': 450000},
'160043100': {'2005-10-26': 340000, '2004-08-25': 229000},
'160060000': {'2011-11-23': 220000, '2015-06-03': 261300},
'160082000': {'2014-10-15': 310000, '2004-12-30': 25000},
'160086000': {'2006-01-04': 230000, '2015-04-03': 230000},
'160087000': {'2015-03-13': 237500, '2006-01-04': 230000},
'160088000': {'2019-05-28': 567500, '2012-02-13': 355132},
'160099100': {'2018-06-01': 190000,
'2005-08-24': 155000,
'2002-07-02': 93500},
'160099201': {'2019-02-01': 412500, '2001-02-09': 97500},
'160099240': {'2012-12-18': 193000,
'2003-07-24': 104000,
'2006-05-16': 210000},
'160099600': {'2020-01-03': 225000,
'2004-11-29': 122000,
'2006-05-31': 212000},
'160105000': {'2006-05-15': 358000, '2003-01-06': 265500},
'160118000': {'2004-06-07': 255000,
'2014-01-09': 297000,
'2003-08-18': 189000},
'160126000': {'2008-12-30': 212000,
'2019-07-09': 460000,
'2014-08-27': 232500,
'2016-01-29': 420000},
'160128150': {'2006-11-06': 145403, '2019-08-12': 328974},
'160128160': {'2013-02-28': 125000,
'2007-02-01': 144900,
'2019-03-05': 149900},
'160128170': {'2006-07-14': 165000, '2015-08-24': 111000},
'160128200': {'2016-07-21': 105325, '2006-03-28': 139900},
'160128201': {'2006-07-14': 165000, '2014-08-18': 127500},
'160128210': {'2006-07-14': 165000,
'2014-12-30': 123000,
'2018-06-25': 210000},
'160128220': {'2006-07-14': 165000, '2010-06-02': 137000},
'160128230': {'2006-07-14': 165000, '2012-03-09': 115000},
'160128240': {'2015-03-20': 115080,
'2018-01-16': 160000,
'2006-07-14': 165000},
'160128260': {'2008-05-27': 148000, '2011-09-20': 160000},
'160128270': {'2016-02-09': 105500, '2009-05-18': 105000},
'160128280': {'2006-05-31': 129900,
'2015-10-09': 96000,
'2019-07-16': 139900,
'2007-09-12': 129500},
'160128290': {'2010-12-28': 132000, '2006-07-14': 165000},
'160128310': {'2006-03-10': 389700, '2011-04-01': 230000},
'160128320': {'2011-04-01': 230000, '2006-03-10': 389700},
'160128340': {'2007-10-26': 171500, '2006-07-14': 165000},
'160128370': {'2006-06-09': 1685700, '2011-12-22': 95000},
'160128390': {'2013-10-09': 141900, '2006-06-09': 129900},
'160128400': {'2009-05-07': 224000, '2009-09-02': 110000},
'160128401': {'2009-10-09': 406000,
'2006-06-09': 1685700,
'2015-03-30': 106000},
'160128410': {'2010-08-03': 120000, '2006-06-09': 1685700},
'160128420': {'2013-04-26': 160000, '2007-02-01': 189800},
'160128430': {'2006-03-28': 139900, '2016-07-21': 105325},
'160128440': {'2015-07-01': 109000,
'2009-10-09': 406000,
'2006-06-09': 1685700},
'160128450': {'2008-02-29': 171870,
'2006-06-09': 1685700,
'2019-06-06': 185000},
'160128460': {'2019-10-21': 180000, '2007-05-21': 164900},
'160128480': {'2006-06-09': 1685700, '2010-03-18': 218000},
'160128490': {'2015-09-30': 165000, '2007-08-15': 228784},
'160128500': {'2006-09-08': 147900, '2018-04-11': 139700},
'160128520': {'2006-06-09': 1685700, '2011-12-01': 91500},
'160128530': {'2009-10-09': 406000,
'2015-03-03': 129000,
'2006-06-09': 1685700,
'2016-12-22': 288000},
'160128540': {'2013-08-14': 156900, '2008-06-19': 187450},
'160128550': {'2007-04-03': 189900, '2017-03-31': 155000},
'160128560': {'2009-10-09': 406000,
'2006-06-09': 1685700,
'2016-12-22': 288000,
'2015-05-08': 134000},
'160128570': {'2010-03-18': 218000, '2006-06-09': 1685700},
'160128580': {'2006-12-05': 190355, '2010-07-08': 149000},
'160128590': {'2008-07-07': 217500, '2007-06-11': 160000},
'160128600': {'2006-03-10': 129900, '2013-06-20': 127500},
'160128601': {'2008-06-16': 159900, '2006-06-09': 1685700},
'160128620': {'2014-02-07': 100000,
'2017-06-02': 115000,
'2006-07-24': 129900},
'160128630': {'2013-10-24': 168000,
'2018-05-16': 186000,
'2008-12-22': 147900},
'160128640': {'2007-03-28': 142900, '2011-09-30': 115000},
'160128650': {'2013-06-20': 135000, '2006-03-10': 389700},
'160128670': {'2019-08-14': 176000, '2007-07-13': 164900},
'160128800': {'2012-10-31': 155312, '2007-03-22': 192900},
'160128900': {'2006-05-12': 142900,
'2010-08-11': 166000,
'2008-05-09': 202000},
'160129100': {'2007-01-16': 517500, '2015-03-03': 160000},
'160129101': {'2019-06-28': 205000, '2007-12-21': 170072},
'160129110': {'2008-02-01': 172000, '2016-05-18': 155000},
'160129120': {'2007-05-30': 169653, '2012-12-19': 147000},
'160129150': {'2019-11-06': 190000,
'2015-01-22': 138000,
'2006-04-06': 179700},
'160129160': {'2015-06-24': 166000,
'2011-08-09': 160000,
'2008-07-01': 199900},
'160129200': {'2007-01-16': 517500,
'2018-11-01': 167000,
'2015-03-31': 165000},
'160129210': {'2006-10-17': 169900,
'2016-12-06': 206500,
'2020-05-27': 229000},
'160129220': {'2019-10-28': 225000, '2006-08-21': 164803},
'160129230': {'2006-03-17': 168201, '2016-03-01': 135000},
'160129240': {'2009-07-02': 152000,
'2015-12-07': 140000,
'2006-04-18': 179900},
'160129260': {'2019-11-26': 120000,
'2007-08-14': 144000,
'2006-06-29': 139900},
'160129270': {'2006-05-31': 169900, '2020-06-09': 98500},
'160129280': {'2012-11-07': 142500, '2006-03-17': 168201},
'160129290': {'2013-05-09': 175000, '2007-06-29': 165000},
'160129300': {'2007-01-16': 517500, '2017-06-15': 174600},
'160129301': {'2011-12-29': 150527, '2006-03-30': 194900},
'160129320': {'2012-10-16': 114570,
'2012-12-18': 123000,
'2006-09-08': 139900},
'160129340': {'2007-07-02': 191000, '2019-11-19': 210000},
'160129360': {'2011-05-20': 174500, '2006-08-30': 179900},
'160129400': {'2007-01-16': 517500, '2017-06-15': 160000},
'160129401': {'2018-06-01': 201000, '2007-08-01': 199900},
'160129410': {'2012-01-18': 146000, '2006-07-28': 169900},
'160129420': {'2014-05-30': 184000, '2006-06-19': 184900},
'160129430': {'2006-05-23': 139900,
'2009-07-01': 139000,
'2020-01-31': 150000},
'160129460': {'2006-12-27': 162400, '2017-07-10': 161000},
'160129501': {'2007-07-10': 139900,
'2015-05-19': 159028,
'2015-08-26': 96000},
'160129520': {'2006-10-17': 164900, '2016-12-13': 192500},
'160129530': {'2014-01-31': 175000, '2008-12-04': 189000},
'160129560': {'2006-05-08': 150253, '2008-09-16': 10000},
'160129590': {'2010-09-07': 146062,
'2010-10-22': 135000,
'2018-12-07': 211750,
'2007-01-23': 180400},
'160129601': {'2013-08-08': 168000,
'2019-03-08': 215000,
'2006-08-08': 174500},
'160129610': {'2007-07-19': 904200, '2010-09-17': 112280},
'160129640': {'2014-04-17': 135000,
'2016-09-02': 150000,
'2006-08-02': 174503},
'160129650': {'2007-01-23': 177000, '2017-09-15': 190000},
'160129660': {'2010-11-01': 224000, '2006-08-28': 214900},
'160129690': {'2017-05-01': 285000,
'2006-05-02': 244900,
'2012-11-26': 195000},
'160129710': {'2010-03-05': 141404,
'2007-03-01': 167000,
'2010-08-02': 149000},
'160129720': {'2006-08-31': 166000, '2017-02-21': 136000},
'160129740': {'2006-08-31': 144900, '2016-07-15': 127000},
'160129780': {'2015-05-29': 245000,
'2007-10-25': 295000,
'2006-07-06': 239900},
'160129790': {'2012-04-30': 92000, '2018-10-19': 109500, '2007-08-29': 99900},
'160129801': {'2007-08-15': 104900, '2015-07-01': 96500},
'160129810': {'2009-09-17': 285000,
'2006-07-06': 239900,
'2007-08-08': 295000,
'2016-06-30': 275000},
'160129820': {'2007-08-10': 209900, '2017-06-30': 212000},
'160129840': {'2008-04-15': 214900, '2017-08-18': 229000},
'160129880': {'2008-05-27': 214900, '2018-06-28': 219000},
'160129890': {'2007-05-25': 215000, '2012-06-05': 172000},
'160129901': {'2018-11-08': 258500, '2007-05-03': 244400},
'160129940': {'2019-08-12': 352826, '2010-04-14': 214000},
'160129950': {'2007-02-09': 195000,
'2012-06-04': 155000,
'2017-04-28': 155000},
'160129960': {'2006-10-30': 194900, '2010-07-07': 186500},
'160129970': {'2008-10-02': 137500, '2016-07-29': 122000},
'160129980': {'2006-11-16': 159900,
'2013-06-26': 122000,
'2013-06-15': 115000},
'160129990': {'2019-06-20': 175000, '2006-07-19': 194900},
'160129999': {'2017-04-19': 225000,
'2007-03-15': 209000,
'2013-08-01': 175000},
'160129I00': {'2008-10-21': 234430,
'2019-04-08': 317500,
'2016-02-19': 320000},
'160129J00': {'2017-07-10': 335000, '2008-06-17': 310000},
'160130101': {'2007-11-15': 179900, '2010-08-09': 157000},
'160130110': {'2019-08-12': 328974, '2008-01-23': 140553},
'160130120': {'2008-01-23': 140553, '2019-08-12': 352826},
'160130140': {'2009-07-29': 110000, '2018-03-02': 124900},
'160130150': {'2008-05-09': 164900, '2017-08-14': 159000},
'160130170': {'2007-12-12': 149900, '2018-06-04': 132000},
'160130180': {'2017-05-11': 125000, '2008-08-05': 147000},
'160130190': {'2006-09-08': 179900, '2018-04-10': 180000},
'160130201': {'2008-01-10': 179900, '2014-08-07': 148000},
'160130210': {'2006-04-04': 145403, '2017-07-06': 126500},
'160130230': {'2006-06-09': 169900, '2009-08-12': 131500},
'160130300': {'2015-06-08': 90000, '2009-05-04': 210000},
'160130400': {'2009-05-04': 210000, '2015-08-05': 93500},
'160130500': {'2008-07-01': 152000, '2020-02-19': 183000},
'160130600': {'2019-06-03': 189000, '2006-03-31': 179900},
'160130700': {'2009-01-29': 115000, '2012-05-07': 130000},
'160130900': {'2008-02-08': 179900, '2020-06-05': 180000},
'160131101': {'2006-07-03': 139900,
'2012-10-31': 117000,
'2016-07-06': 120000},
'160131140': {'2020-01-10': 167500, '2008-08-08': 135000},
'160131150': {'2008-08-01': 135000, '2020-01-30': 145000},
'160131200': {'2015-08-19': 129000,
'2008-08-25': 105000,
'2018-07-20': 142000},
'160131240': {'2012-01-05': 143500,
'2018-05-30': 150000,
'2008-03-03': 134900},
'160131400': {'2009-07-24': 82500, '2018-08-08': 131000},
'160131600': {'2015-06-12': 110000,
'2007-07-17': 135000,
'2013-02-19': 137000},
'160131700': {'2018-09-24': 130000, '2008-06-26': 158500},
'170003000': {'2005-08-26': 213325,
'2000-02-11': 149000,
'2014-08-22': 325950,
'2004-07-22': 284000},
'170009700': {'2002-12-19': 250000, '2005-08-18': 198431},
'170018000': {'2019-04-16': 468000,
'2018-08-22': 439330,
'2019-05-30': 963418},
'170018100': {'2014-02-04': 345000,
'2015-09-30': 540000,
'2019-10-10': 635000},
'170018200': {'2015-12-18': 175000, '2014-02-04': 345000},
'170018300': {'2014-02-04': 345000, '2015-12-18': 460000},
'170019000': {'2011-12-28': 268000, '2004-06-09': 305000},
'170021000': {'2005-01-21': 320000,
'2019-02-01': 372500,
'2013-10-25': 300000},
'170031000': {'2000-04-18': 129500, '2004-06-21': 225000},
'170036000': {'2007-04-10': 725000,
'2002-08-29': 382500,
'2018-11-26': 684000},
'170037000': {'2018-11-26': 684000,
'2002-08-29': 382500,
'2007-04-10': 725000},
'170037100': {'2002-08-29': 382500,
'2018-11-26': 642000,
'2007-04-10': 725000},
'170044000': {'2000-07-28': 135000, '2018-04-25': 228000},
'170045000': {'2016-12-05': 325000, '2016-06-20': 217000},
'170050000': {'2016-02-26': 360000, '2006-11-30': 437000},
'170053000': {'2016-12-28': 368000, '2019-12-19': 399900},
'170055000': {'2005-03-29': 200000, '2002-08-30': 148000},
'170057500': {'2011-06-22': 160000,
'2003-07-02': 175100,
'2008-07-01': 213912},
'170062000': {'2016-09-09': 152000, '2016-12-16': 295500},
'170065000': {'2015-03-09': 275000,
'2002-12-03': 210000,
'2016-06-13': 315000,
'2004-10-27': 265000},
'170065A00': {'2006-02-24': 200000,
'2005-07-01': 125000,
'2006-11-03': 429000},
'170066000': {'2013-03-23': 251858,
'2013-12-06': 224900,
'2005-11-22': 395000},
'170067000': {'2012-12-18': 175000,
'2012-08-21': 155836,
'2005-11-22': 285000},
'170080100': {'2004-01-09': 82750, '2010-02-11': 144000},
'170080101': {'2004-01-07': 102500, '2001-01-16': 55000},
'170080110': {'2002-02-08': 59000, '2002-10-24': 76000},
'170080120': {'2001-10-24': 62000,
'2017-03-17': 155000,
'2004-05-28': 113000},
'170080130': {'2005-04-20': 145000, '2010-07-14': 130000},
'170080140': {'2003-03-19': 85500, '2003-11-10': 120000},
'170080160': {'2015-10-02': 165000,
'2004-09-30': 129900,
'2008-12-30': 170000},
'170080170': {'2007-01-17': 155000,
'2004-05-07': 125000,
'2013-04-08': 158000},
'170080200': {'2004-06-29': 126600,
'2006-04-26': 164900,
'2019-05-17': 162000,
'2013-08-01': 150000},
'170080201': {'2001-01-22': 50868, '2003-04-01': 87000},
'170080300': {'2002-09-17': 85000, '2004-07-02': 120000},
'170080500': {'2012-01-18': 150000,
'2004-04-01': 112000,
'2017-06-12': 172500},
'170080700': {'2006-12-13': 170000,
'2003-11-25': 95000,
'2004-06-29': 124900},
'170080800': {'2004-07-29': 128000, '2004-03-24': 113000},
'170080900': {'2001-12-05': 60000, '2004-01-07': 97500},
'170091300': {'2007-02-27': 1871356, '2006-06-20': 660000},
'170094000': {'2006-07-24': 330000,
'2016-04-04': 350000,
'2019-02-28': 395000,
'2004-05-26': 300000,
'2011-06-24': 325000,
'2000-06-30': 115000},
'170103000': {'2003-01-03': 232000,
'2000-11-03': 130000,
'2005-01-06': 3000000},
'170106000': {'2010-08-31': 275000,
'2003-11-20': 140000,
'2018-06-20': 375000},
'170107000': {'2018-09-13': 150000, '2004-01-21': 149000},
'170112000': {'2018-01-18': 304660, '2011-05-02': 300000},
'170113000': {'2010-12-28': 373900, '2018-01-18': 398240},
'170114000': {'2017-08-17': 273000, '2006-03-01': 110000},
'17A003000': {'2015-10-14': 205000, '2009-08-10': 145000},
'17A004000': {'2009-08-10': 145000, '2015-10-14': 205000},
'17A006000': {'2009-12-23': 400000, '2002-10-04': 199900},
'17A007000': {'2014-06-26': 235000, '2002-10-04': 199900},
'17A018000': {'2009-12-17': 135061, '2010-06-09': 95000},
'17A019000': {'2004-02-02': 212036,
'2005-02-15': 257000,
'2019-04-30': 640000},
'17A019A00': {'2004-02-02': 212036,
'2005-02-15': 257000,
'2019-04-30': 640000},
'17A020000': {'2005-02-08': 250000,
'2018-01-03': 315000,
'2004-02-02': 212036,
'2007-08-01': 297500},
'17A021000': {'2004-02-02': 188891,
'2011-06-23': 138000,
'2011-03-14': 166960},
'17A022000': {'2001-04-20': 85000, '2018-11-05': 192500},
'17A025000': {'2010-06-09': 95000, '2009-12-17': 135061},
'17A028000': {'2006-07-14': 510000, '2016-08-01': 444375},
'17A040000': {'2007-11-28': 228000,
'2003-03-03': 125000,
'2009-06-18': 189900},
'17A043000': {'2018-06-28': 392000, '2017-03-27': 136000},
'17A044000': {'2000-04-14': 88000, '2001-09-25': 93000},
'17A045000': {'2013-06-10': 227000,
'2001-01-03': 119000,
'2016-06-28': 259900},
'17A048000': {'2005-03-28': 272000, '2019-06-28': 327850},
'17A049100': {'2015-09-14': 177715,
'2004-07-16': 268000,
'2018-06-13': 420000,
'2019-11-20': 420000,
'2016-04-28': 250000,
'2006-06-07': 314500},
'17A053100': {'2002-08-09': 195000, '2000-06-30': 143900},
'17A055000': {'2019-05-01': 321000,
'2005-07-01': 250000,
'2016-04-28': 251500,
'2012-09-07': 209000},
'17A062000': {'2013-09-16': 190000,
'2004-09-30': 180000,
'2001-10-31': 85000},
'180004000': {'2004-06-25': 200000,
'2017-07-28': 219600,
'2013-07-26': 175000,
'2018-03-01': 260000,
'2006-09-27': 230000},
'180005200': {'2007-08-07': 310000, '2003-09-30': 240000},
'180005400': {'2005-09-02': 230000,
'2015-06-15': 195000,
'2001-01-09': 122500},
'180007000': {'2004-04-29': 187900,
'2002-07-02': 114200,
'2009-02-19': 216000},
'180015000': {'2017-10-30': 267750,
'2014-07-31': 233000,
'2007-03-16': 255000},
'180015101': {'2006-12-05': 240000, '2003-09-25': 140000},
'180015130': {'2001-07-02': 115000, '2018-07-16': 367500},
'180015140': {'2015-08-12': 240000, '2005-12-05': 245000},
'180015210': {'2000-07-28': 147000,
'2015-12-10': 264000,
'2005-05-25': 260000,
'2019-04-25': 325000},
'180015240': {'2000-06-30': 119000, '2005-03-01': 205000},
'180015300': {'2003-08-04': 181500, '2001-07-27': 142500},
'180015301': {'2004-10-15': 249500, '2001-06-04': 138500},
'180015800': {'2012-06-21': 233000,
'2015-05-28': 235000,
'2019-08-07': 325000},
'180017100': {'2005-09-27': 275000, '2000-05-30': 124900},
'180018000': {'2016-02-22': 332500,
'2007-04-24': 335000,
'2008-11-17': 330000,
'2020-02-21': 445000},
'180018100': {'2000-05-25': 98500, '2006-06-30': 215000},
'180021000': {'2019-01-07': 300000,
'2015-03-18': 280000,
'2001-05-31': 197500},
'180024000': {'2000-03-31': 270000, '2017-06-14': 510000},
'180031000': {'2014-08-19': 495000, '2002-08-02': 372000},
'180041000': {'2018-11-16': 325000, '2003-02-12': 200000},
'180044000': {'2000-05-15': 181400, '2016-03-22': 427500},
'180046000': {'2015-09-21': 415000, '2006-06-02': 381000},
'180047000': {'2005-12-20': 495000,
'2003-07-02': 375000,
'2007-10-12': 470000},
'180048000': {'2006-03-09': 199000, '2015-08-14': 262500},
'180048100': {'2003-06-04': 165000,
'2017-06-21': 260000,
'2012-07-24': 185000},
'180055000': {'2004-05-07': 131000, '2003-10-08': 75000},
'180055100': {'2007-10-29': 100800, '2011-03-08': 95000},
'180055400': {'2011-08-05': 135000, '2012-06-01': 108800},
'180057000': {'2005-01-04': 282500, '2019-03-07': 455000},
'18A001000': {'2005-10-06': 217500,
'2004-05-11': 150000,
'2016-05-09': 210000,
'2001-10-02': 108000},
'18A007000': {'2008-01-15': 154500, '2017-05-10': 160000},
'18A020000': {'2005-06-06': 323000, '2008-09-03': 315000},
'18A022000': {'2013-06-27': 293000,
'2000-11-01': 150000,
'2019-02-08': 440000},
'18A022100': {'2000-07-13': 115000, '2017-09-15': 580000},
'18A023100': {'2015-09-09': 800000, '2017-08-11': 305000},
'18A023200': {'2012-03-21': 296000,
'2001-01-16': 126800,
'2017-06-02': 375000},
'18A026000': {'2008-05-30': 280000, '2019-06-19': 358000},
'18A028000': {'2017-12-13': 220000,
'2017-09-14': 139850,
'2005-02-16': 200000},
'18A032000': {'2003-12-10': 164800,
'2013-08-12': 190000,
'2000-11-28': 18000,
'2007-09-27': 180000},
'18A036000': {'2000-11-28': 40000, '2009-09-16': 612500},
'18A037000': {'2009-09-16': 612500, '2000-11-28': 40000},
'18A052000': {'2005-12-29': 160000,
'2016-08-08': 425000,
'2007-01-30': 450000},
'18A053000': {'2006-07-10': 474900,
'2010-07-07': 373500,
'2005-12-29': 160000},
'18A054000': {'2013-04-11': 362000,
'2006-05-31': 434900,
'2008-06-23': 390000},
'18A056000': {'2019-03-07': 440000,
'2007-01-16': 80000,
'2007-04-26': 456000},
'18A061000': {'2009-07-02': 395000, '2019-06-07': 488000},
'18A063000': {'2019-06-12': 435000, '2011-04-20': 350000},
'18A065000': {'2011-08-19': 416602, '2016-08-09': 440000},
'18A066000': {'2020-06-02': 515000, '2012-05-10': 390000},
'18A068000': {'2019-03-04': 435000,
'2012-11-16': 366692,
'2015-07-29': 382000},
'18A070000': {'2019-06-18': 469000, '2014-03-28': 392993},
'18A074000': {'2014-07-11': 377009, '2019-04-22': 435750},
'18A076B00': {'2016-05-04': 296000, '2015-05-22': 271657},
'18A077B00': {'2019-06-20': 354000, '2015-05-18': 271657},
'18A081000': {'2016-06-07': 399000, '2020-05-12': 474900},
'18A093000': {'2018-12-05': 230000, '2019-05-31': 428000},
'18A094000': {'2020-04-14': 484196, '2019-08-22': 345000},
'18A095000': {'2019-08-22': 345000, '2020-02-07': 468530},
'18A096000': {'2019-12-16': 487518, '2019-07-12': 115000},
'18A097000': {'2019-11-19': 509928, '2019-05-23': 115000},
'18A098000': {'2020-02-24': 459150, '2019-08-22': 345000},
'18A099000': {'2018-12-05': 230000, '2019-12-02': 495475},
'18A100000': {'2018-06-01': 115000, '2019-11-01': 459900},
'18A101000': {'2018-06-01': 115000, '2019-01-17': 482637},
'18A104000': {'2018-02-08': 460000, '2019-03-14': 468000},
'18A108000': {'2018-04-02': 447000, '2015-06-10': 416360},
'18A111000': {'2015-08-24': 392500,
'2020-05-19': 489000,
'2013-09-13': 394750,
'2018-09-06': 425000},
'18A113000': {'2019-07-09': 315000, '2013-11-27': 271657},
'18A113B00': {'2016-06-08': 318000,
'2019-04-18': 360000,
'2013-12-18': 271657},
'18A114000': {'2013-05-31': 271657, '2016-05-12': 320000},
'18A115000': {'2012-10-17': 386900, '2020-06-16': 490000},
'18A119000': {'2011-08-01': 334900, '2015-10-09': 365000},
'18A120000': {'2013-08-01': 385000,
'2011-10-12': 357900,
'2020-03-19': 495000},
'18A121000': {'2016-07-01': 407000,
'2012-08-02': 355000,
'2010-10-28': 360000,
'2017-05-01': 442000},
'18A122000': {'2014-07-02': 390000, '2010-11-29': 362000},
'18A123000': {'2018-04-02': 461000,
'2016-05-25': 409900,
'2009-10-08': 389900},
'18A124000': {'2009-07-14': 368000, '2013-07-24': 388000},
'18A128000': {'2019-03-08': 525000,
'2005-07-13': 240000,
'2019-08-15': 392131},
'18A129000': {'2005-07-13': 160000,
'2019-03-08': 525000,
'2019-12-02': 477484},
'18A130000': {'2019-03-08': 525000,
'2019-08-29': 477288,
'2005-07-13': 160000},
'18A131000': {'2006-04-10': 550000,
'2008-02-27': 505000,
'2016-04-01': 445000,
'2006-09-27': 110000},
'18A132000': {'2007-06-04': 499900,
'2013-06-04': 379500,
'2006-04-10': 550000},
'18A133000': {'2006-04-10': 550000,
'2010-01-25': 393000,
'2017-02-24': 393000},
'18A134000': {'2006-10-23': 220000,
'2010-11-02': 360000,
'2008-09-19': 444000,
'2018-03-13': 400000},
'18A135000': {'2006-10-23': 220000,
'2014-05-29': 450000,
'2008-06-17': 430000},
'18A136000': {'2012-08-14': 390000,
'2007-03-12': 220000,
'2014-04-29': 370405},
'18A137000': {'2012-08-14': 390000,
'2007-03-12': 220000,
'2014-04-28': 341900},
'18A152000': {'2012-03-28': 338500,
'2018-05-22': 439900,
'2008-08-29': 379000,
'2007-02-13': 550000},
'18A153000': {'2008-06-30': 425900, '2006-07-21': 220000},
'18A154000': {'2006-07-21': 220000, '2011-08-12': 340000},
'18A155000': {'2007-02-13': 550000,
'2018-07-16': 349000,
'2012-08-14': 390000,
'2015-11-20': 290000},
'18A156000': {'2007-02-13': 550000,
'2012-08-14': 390000,
'2015-08-12': 318000},
'18A157000': {'2012-08-14': 390000,
'2018-07-31': 341000,
'2015-05-19': 320285,
'2007-02-13': 550000},
'18A158000': {'2012-08-31': 309000, '2006-04-10': 550000},
'18A159000': {'2012-08-14': 63000,
'2019-05-15': 351000,
'2016-03-24': 295000,
'2006-04-10': 550000},
'18A160000': {'2012-08-14': 390000,
'2015-12-30': 290000,
'2007-02-13': 550000},
'190001000': {'2018-06-11': 447000, '2011-05-13': 430500},
'190008000': {'2004-03-31': 205000, '2015-03-27': 229960},
'190010000': {'2003-03-21': 340000,
'2019-01-31': 400000,
'2000-08-01': 275000},
'190016300': {'2019-01-07': 348000, '2007-07-11': 300900},
'190018000': {'2012-07-24': 580000, '2000-08-10': 272000},
'190020000': {'2012-06-25': 500000,
'2000-02-02': 446900,
'2015-05-19': 460000},
'190021000': {'2015-06-12': 460000, '2005-01-21': 380000},
'190027000': {'2004-04-07': 247000,
'2013-06-14': 422000,
'2006-08-18': 388500},
'190034110': {'2008-07-17': 281000,
'2000-01-05': 131000,
'2013-04-26': 305000},
'190034130': {'2003-07-16': 180000,
'2006-07-10': 270500,
'2014-08-07': 268250},
'190034150': {'2008-04-30': 249000,
'2018-11-15': 299900,
'2014-04-18': 248850},
'190034160': {'2005-07-29': 267000, '2016-12-19': 271000},
'190034180': {'2017-07-20': 297000, '2000-05-26': 125000},
'190034190': {'2009-10-14': 199900,
'2004-07-23': 205000,
'2011-08-31': 215000},
'190034260': {'2013-01-29': 170000, '2004-08-05': 245000},
'190034280': {'2002-07-29': 168500, '2005-07-08': 250000},
'190034301': {'2014-01-03': 118600,
'2007-05-24': 280000,
'2018-04-02': 275000},
'190034310': {'2006-07-31': 284900, '2001-06-18': 135000},
'190035120': {'2018-10-17': 302500,
'2005-09-20': 264000,
'2000-07-05': 163000},
'190035150': {'2020-04-27': 360000,
'2011-03-31': 236500,
'2014-01-06': 8750,
'2018-07-31': 328000},
'190035170': {'2000-06-15': 117500,
'2018-03-29': 349500,
'2007-01-23': 326500,
'2014-06-30': 291000},
'190035190': {'2008-06-11': 220000, '2020-03-25': 322500},
'190035201': {'2018-09-21': 280000, '2014-01-31': 205000},
'190035230': {'2005-07-25': 290000, '2000-10-05': 130000},
'190035240': {'2008-10-01': 232000,
'2000-09-29': 136000,
'2018-05-25': 283000},
'190035260': {'2003-07-31': 196000,
'2000-08-17': 132000,
'2019-05-03': 235000},
'190035270': {'2006-03-31': 317500,
'2016-04-14': 294195,
'2017-01-05': 199000},
'190035280': {'2004-08-25': 215500, '2016-05-24': 248000},
'190035290': {'2019-04-15': 338000, '2005-07-26': 194280},
'190035310': {'2018-10-12': 295000,
'2013-06-14': 147266,
'2014-07-15': 177100,
'2014-11-07': 263000,
'2002-07-31': 150000},
'190035320': {'2017-09-14': 205000, '2019-07-24': 265000},
'190035340': {'2006-06-06': 273000, '2002-06-19': 162000},
'190035350': {'2018-03-29': 348900,
'2011-06-29': 283500,
'2006-06-28': 325000,
'2004-05-03': 220000,
'2014-11-18': 296000},
'190035360': {'2005-07-01': 224000,
'2003-07-23': 175900,
'2009-01-20': 251000},
'190035600': {'2019-04-11': 308000, '2016-08-29': 245000},
'190035900': {'2012-05-02': 350000, '2006-06-29': 375000},
'190036500': {'2004-06-30': 189900, '2002-06-28': 148000},
'190036700': {'2004-10-29': 210000,
'2011-08-02': 240000,
'2006-06-13': 265000},
'190037100': {'2006-05-09': 259900,
'2003-06-10': 210000,
'2016-05-02': 264000},
'190037101': {'2018-02-28': 338000,
'2000-06-29': 125000,
'2009-04-08': 227000,
'2004-07-26': 205900},
'190037110': {'2013-06-10': 150000,
'2014-07-11': 284900,
'2016-05-09': 5000,
'2005-06-08': 286000,
'2001-06-04': 150000,
'2003-06-03': 212500},
'190037150': {'2016-09-08': 192000, '2006-08-01': 178650},
'190037180': {'2002-06-14': 136000, '2008-03-27': 229000},
'190037220': {'2014-05-16': 152589, '2014-07-11': 122757},
'190037240': {'2006-08-04': 260000, '2017-10-17': 258000},
'190037270': {'2003-06-16': 156000,
'2012-10-31': 235000,
'2016-04-25': 275000},
'190037280': {'2014-12-29': 182500, '2018-02-12': 250000},
'190037300': {'2020-03-27': 320000,
'2011-04-26': 280000,
'2006-08-23': 290000},
'190037330': {'2005-04-12': 205000,
'2018-07-18': 255000,
'2003-04-15': 119925,
'2006-08-10': 230000},
'190037340': {'2000-02-29': 92760, '2005-02-01': 194000},
'190037350': {'2002-07-31': 145000,
'2004-05-18': 185000,
'2009-07-28': 237000,
'2006-07-27': 243800},
'190037500': {'2000-06-05': 115000,
'2005-07-27': 239900,
'2019-07-10': 313500,
'2014-07-01': 242400},
'190037600': {'2017-12-21': 230000,
'2006-10-23': 184500,
'2007-03-02': 180000},
'190037900': {'2001-05-09': 95000, '2004-07-26': 127700},
'190038000': {'2001-01-02': 140000, '2013-06-24': 262500},
'190042000': {'2002-11-27': 195000, '2000-06-08': 125000},
'190045000': {'2007-06-14': 316000,
'2002-08-15': 189500,
'2015-06-01': 352000},
'190048000': {'2008-03-20': 309000,
'2010-12-14': 367500,
'2005-04-04': 220000},
'190050000': {'2015-06-16': 223500,
'2001-06-08': 129400,
'2003-02-03': 149000,
'2009-07-30': 257000},
'190051000': {'2012-10-12': 315000,
'2009-05-22': 367000,
'2004-08-30': 259900},
'190052A00': {'2007-07-10': 96030,
'2018-04-30': 349000,
'2009-09-04': 65000,
'2012-02-29': 274900},
'190054000': {'2020-03-02': 271000,
'2005-07-13': 199900,
'2003-03-10': 140000},
'190056000': {'2018-07-02': 360000, '2012-10-24': 194900},
'190059000': {'2006-04-12': 247000, '2004-01-23': 169900},
'190060000': {'2008-07-24': 265000, '2004-11-03': 184000},
'190061000': {'2018-10-11': 315000, '2004-07-28': 184000},
'190062000': {'2005-01-31': 220000,
'2004-06-30': 224500,
'2001-11-09': 157500},
'190065000': {'2000-06-20': 144000, '2004-06-21': 230000},
'190069000': {'2017-02-02': 250000, '2012-12-18': 170000},
'190076000': {'2011-06-21': 230000, '2003-12-22': 227000},
'190077000': {'2010-04-05': 175000, '2005-04-29': 207500},
'200004000': {'2014-02-04': 310000, '2019-06-26': 368500},
'200005000': {'2012-06-13': 270000,
'2004-06-01': 235000,
'2001-06-28': 168000},
'200006000': {'2019-09-24': 410500,
'2012-06-14': 290000,
'2008-06-20': 345000,
'2001-06-29': 98000,
'2005-08-16': 335000},
'200008000': {'2008-07-17': 222500, '2015-12-09': 245000},
'200009000': {'2003-07-08': 180000,
'2011-10-14': 181000,
'2001-07-03': 105000},
'200014100': {'2003-12-12': 143000, '2003-04-30': 126000},
'200019000': {'2015-07-10': 85000,
'2015-10-08': 650000,
'2018-10-26': 725000,
'2017-12-28': 103000},
'200019002': {'2018-09-12': 95000, '2019-05-01': 592847},
'200020000': {'2014-02-11': 100000, '2017-06-30': 240000},
'200022000': {'2017-05-26': 596990,
'2007-07-11': 375000,
'2012-12-06': 39991,
'2013-10-11': 70000,
'2007-02-07': 160000,
'2015-10-08': 650000},
'200022001': {'2013-10-11': 70000,
'2015-10-08': 650000,
'2017-08-15': 600000},
'200022002': {'2017-05-26': 590920,
'2013-10-11': 70000,
'2015-10-08': 650000,
'2016-12-20': 110000},
'200023000': {'2015-10-08': 650000,
'2017-04-05': 678001,
'2014-12-24': 30500,
'2007-11-21': 111000},
'200024000': {'2007-11-21': 111000,
'2014-12-24': 30500,
'2015-10-08': 650000},
'200025001': {'2015-10-08': 650000, '2018-02-01': 478441},
'200025003': {'2015-10-08': 650000, '2017-01-06': 647052},
'200026000': {'2006-05-26': 192000, '2012-07-13': 180000},
'200028000': {'2016-12-09': 280500, '2016-03-31': 170000},
'200028100': {'2004-07-27': 215000,
'2000-12-06': 90000,
'2017-07-06': 306000,
'2006-07-27': 279000},
'200028200': {'2018-06-07': 230000, '2018-10-09': 315000},
'200031000': {'2007-11-15': 232000, '2009-04-30': 198900},
'200033000': {'2009-11-06': 200000,
'2019-11-05': 305000,
'2018-04-23': 283950,
'2003-12-19': 32000},
'200033A00': {'2009-09-08': 231000,
'2006-10-23': 100000,
'2010-08-03': 260000},
'200035000': {'2019-06-25': 247000,
'2016-01-05': 205000,
'2001-11-12': 90500},
'200037000': {'2010-09-10': 190000, '2017-08-21': 290000},
'200038000': {'2015-09-22': 328000,
'2012-01-26': 155000,
'2012-06-11': 282800},
'200039000': {'2006-11-07': 115165, '2015-07-15': 375000},
'200041000': {'2006-07-12': 237500,
'2015-01-23': 209500,
'2019-01-22': 317000},
'200044000': {'2018-10-04': 292500,
'2011-06-16': 186000,
'2014-05-23': 235000,
'2020-05-01': 319000},
'200051000': {'2008-08-27': 270000, '2005-06-07': 245000},
'200052000': {'2016-06-06': 235000,
'2017-09-12': 275000,
'2001-04-17': 135000},
'200060000': {'2000-11-03': 132000,
'2001-08-22': 155000,
'2004-05-24': 239900,
'2011-12-29': 250000},
'200061000': {'2017-06-29': 539250,
'2016-12-29': 105000,
'2016-08-24': 41000},
'200062000': {'2018-05-25': 567000,
'2015-02-26': 45000,
'2015-10-08': 650000,
'2016-12-29': 105000},
'200063000': {'2015-10-08': 650000,
'2018-09-04': 566519,
'2015-02-26': 45000},
'200067000': {'2004-12-30': 242500, '2006-07-27': 358000},
'200070000': {'2006-10-31': 448500,
'2019-01-17': 520000,
'2008-11-04': 380725,
'2002-06-03': 270000,
'2008-11-13': 275000},
'200071000': {'2013-12-31': 228000,
'2018-03-09': 245000,
'2010-12-17': 230000},
'200077100': {'2006-06-23': 522500, '2004-05-25': 293330},
'200083000': {'2015-05-15': 420000, '2001-07-10': 175000},
'200083100': {'2007-04-30': 292000,
'2014-06-17': 270000,
'2004-07-28': 209000,
'2020-06-12': 337000},
'200086000': {'2009-09-17': 228000, '2015-05-26': 315000},
'200096000': {'2015-03-18': 359000, '2004-01-21': 265000},
'200101000': {'2014-06-25': 318000, '2000-05-26': 189900},
'200102000': {'2016-01-20': 191000,
'2009-02-17': 162500,
'2019-07-15': 203000},
'200105000': {'2008-05-30': 208000, '2014-07-14': 221250},
'200106000': {'2014-08-29': 270000,
'2003-01-29': 164450,
'2018-05-30': 318000},
'200110000': {'2001-09-21': 80000, '2013-09-19': 115000},
'200111000': {'2008-07-31': 280000,
'2013-11-08': 265000,
'2006-08-04': 320000},
'200113100': {'2005-04-08': 350000,
'2004-02-26': 191000,
'2006-02-24': 385000,
'2008-09-29': 390000},
'200115000': {'2004-12-30': 166800, '2000-07-31': 85000},
'200116000': {'2005-07-12': 289000,
'2001-06-28': 172000,
'2003-02-13': 225000},
'200118000': {'2000-01-28': 95000,
'2006-05-30': 238000,
'2015-05-29': 264000},
'200119000': {'2003-06-17': 197000,
'2009-01-30': 315000,
'2015-09-30': 370000,
'2018-04-20': 425000},
'200123000': {'2000-02-02': 446900, '2012-06-25': 500000},
'200124000': {'2002-05-24': 249900,
'2001-02-05': 199500,
'2017-07-31': 385000},
'200126200': {'2000-05-01': 122500,
'2006-04-03': 260000,
'2015-06-30': 254000,
'2011-03-31': 239250},
'200127000': {'2017-01-27': 390000, '2016-08-04': 182000},
'200131000': {'2012-07-25': 340000,
'2010-07-13': 343000,
'2000-05-30': 126000},
'200132000': {'2018-06-06': 500000,
'2000-10-05': 122500,
'2009-10-20': 394900},
'200136000': {'2019-05-16': 405000,
'2012-07-17': 298000,
'2001-12-03': 159500,
'2013-05-16': 305000},
'200139000': {'2014-11-19': 113559,
'2015-03-30': 100000,
'2015-02-24': 75000,
'2018-06-28': 409000},
'200141000': {'2019-06-14': 445000,
'2004-07-09': 200000,
'2003-10-31': 180000,
'2013-07-16': 370000},
'200153000': {'2020-05-19': 369000, '2017-09-26': 275000},
'200154000': {'2017-05-15': 335000, '2005-07-07': 250000},
'200155000': {'2015-06-03': 247000, '2006-02-28': 230000},
'200158000': {'2016-09-01': 307000,
'2007-05-10': 249900,
'2015-01-09': 159129,
'2015-06-26': 154875,
'2017-09-26': 151147},
'200160000': {'2019-10-31': 306000,
'2019-07-30': 242500,
'2000-12-29': 115000},
'200163000': {'2001-05-11': 143500, '2007-06-13': 279000},
'200180000': {'2006-07-03': 276000,
'2000-08-07': 140500,
'2002-06-28': 186000},
'200194000': {'2017-06-02': 319000, '2016-09-19': 219000},
'200200000': {'2010-05-04': 215000,
'2004-07-16': 166600,
'2016-06-16': 249900,
'2008-09-08': 190000},
'200203000': {'2000-02-29': 116250, '2003-07-18': 175000},
'200207000': {'2002-10-09': 150000,
'2014-10-29': 204000,
'2000-06-29': 139500},
'200208000': {'2019-09-09': 250000, '2019-05-02': 197000},
'200209000': {'2011-11-10': 195000, '2019-04-19': 292000},
'200210000': {'2016-05-20': 232000, '2016-02-25': 160000},
'200216000': {'2011-05-03': 182000, '2017-04-18': 242000},
'200220000': {'2015-05-22': 245000, '2017-03-07': 1000, '2019-11-22': 340000},
'200222000': {'2009-07-09': 220000,
'2005-09-15': 230000,
'2016-11-28': 259500},
'200223000': {'2017-04-27': 172000,
'2019-06-05': 360000,
'2018-12-10': 12000},
'200226000': {'2000-05-09': 127680, '2017-09-25': 239500},
'200236000': {'2017-06-28': 317500,
'2002-03-21': 154500,
'2004-03-22': 199500},
'200237000': {'2004-07-23': 237000,
'2003-10-31': 142000,
'2008-03-10': 253500,
'2016-07-28': 310000,
'2006-08-23': 256000},
'200240000': {'2010-09-10': 197988,
'2004-08-04': 234000,
'2011-04-14': 233500},
'200250000': {'2014-09-05': 187000, '2017-05-30': 213750},
'200254000': {'2001-02-28': 132500,
'2010-04-19': 238284,
'2016-05-18': 352000},
'200255000': {'2013-04-29': 220000,
'2006-04-04': 230000,
'2004-08-03': 210000},
'200257000': {'2009-06-25': 205000,
'2004-02-27': 169900,
'2017-04-11': 283000},
'200259180': {'2014-09-12': 268500,
'2018-12-11': 315000,
'2008-09-16': 265000,
'2008-03-07': 180500},
'200259190': {'2005-08-24': 283000, '2001-07-03': 154900},
'200259200': {'2019-05-24': 348000, '2014-05-02': 230500},
'200259210': {'2011-04-05': 175000, '2015-05-22': 229500},
'200259300': {'2008-11-13': 290000, '2016-03-29': 305000},
'200259800': {'2002-12-06': 184000, '2016-07-21': 325000},
'200259900': {'2010-09-30': 225000,
'2003-03-12': 170000,
'2008-03-24': 225000},
'200261000': {'2018-11-28': 299500,
'2011-12-02': 215000,
'2001-01-19': 281400,
'2000-12-11': 105900},
'200265000': {'2003-03-28': 101000,
'2006-11-30': 150000,
'2002-07-30': 93000},
'200266200': {'2018-04-30': 326820, '2017-12-27': 492679},
'200266300': {'2018-02-16': 297591, '2017-12-27': 492679},
'200266400': {'2017-12-27': 492679, '2018-05-04': 303000},
'200267100': {'2017-12-27': 492679, '2018-04-30': 309900},
'200267200': {'2017-12-27': 369509, '2018-05-07': 322253},
'200267300': {'2017-12-27': 369509, '2018-04-19': 285065},
'200267400': {'2017-12-27': 369509, '2018-05-11': 324900},
'200268100': {'2017-08-17': 1112021, '2017-08-31': 290600},
'200268200': {'2017-08-31': 275057, '2017-08-17': 1112021},
'200268300': {'2017-08-17': 1112021, '2017-08-31': 274900},
'200268400': {'2017-08-31': 274900, '2017-08-17': 1112021},
'200269100': {'2017-08-17': 1112021, '2017-08-31': 308641},
'200269200': {'2017-08-17': 1112021, '2018-03-20': 284900},
'200269300': {'2018-01-18': 288641, '2017-08-17': 1112021},
'200269400': {'2017-08-17': 1112021, '2017-12-21': 299900},
'200270100': {'2017-09-20': 562440, '2017-09-25': 303822},
'200270200': {'2017-09-20': 562440, '2017-09-29': 274900},
'200270300': {'2017-09-20': 562440, '2018-05-25': 302230},
'200270400': {'2018-03-13': 309145, '2017-09-20': 562440},
'200271100': {'2017-12-22': 305878, '2017-09-29': 616700},
'200271200': {'2018-03-23': 271353, '2017-09-29': 616700},
'200271300': {'2018-02-28': 284900, '2017-09-29': 616700},
'200271400': {'2017-09-29': 616700, '2018-04-11': 315000},
'210003000': {'2017-08-21': 250000, '2018-01-11': 306500},
'210007000': {'2002-07-30': 126500,
'2005-08-09': 215000,
'2017-08-01': 175000},
'210008000': {'2006-12-22': 280000,
'2003-05-27': 154000,
'2004-10-18': 216000},
'210011000': {'2008-07-28': 408000,
'2001-01-30': 132000,
'2016-08-01': 395000},
'210013100': {'2007-07-18': 280000, '2012-06-01': 292000},
'210015000': {'2006-06-29': 337000, '2001-05-08': 162000},
'210016000': {'2015-02-26': 235000, '2002-06-03': 160000},
'210020000': {'2017-06-09': 241900, '2005-07-29': 205000},
'210025100': {'2012-05-22': 206000, '2005-07-21': 197000},
'210026000': {'2002-06-13': 209500,
'2009-05-01': 282000,
'2017-04-20': 377330},
'210033000': {'2013-11-21': 326000,
'2019-08-09': 485000,
'2007-08-24': 288000},
'210034000': {'2004-08-05': 288000, '2002-05-28': 24000},
'210035000': {'2012-02-28': 245500, '2008-02-25': 201000},
'210039000': {'2008-09-19': 390000, '2004-08-18': 126000},
'210042000': {'2001-10-25': 148000,
'2003-11-14': 205000,
'2005-08-12': 239250,
'2016-01-21': 280000},
'210043000': {'2019-09-03': 320000, '2003-09-25': 155000},
'210044000': {'2018-02-07': 219312,
'2018-10-26': 255500,
'2017-11-03': 175000,
'2020-06-10': 315000,
'2018-03-29': 165000},
'210047000': {'2006-02-14': 320000,
'2004-01-15': 172000,
'2019-01-28': 342500},
'210049000': {'2005-07-05': 376000,
'2002-04-30': 219900,
'2008-06-20': 402500},
'210053000': {'2004-12-29': 290000,
'2014-06-03': 330000,
'2012-03-30': 263000},
'210055000': {'2014-09-08': 225000, '2001-03-29': 133000},
'210058000': {'2007-05-21': 355000, '2020-04-03': 402000},
'210059000': {'2014-03-24': 398900, '2008-09-12': 97000, '2008-06-11': 97000},
'210060000': {'2005-12-19': 39000,
'2011-12-19': 100000,
'2015-05-20': 515000},
'210061100': {'2012-12-28': 342000,
'2018-11-28': 480000,
'2005-07-22': 310000},
'210062000': {'2003-11-06': 130000, '2012-08-30': 235000},
'210064000': {'2009-07-24': 410000, '2001-06-11': 180000},
'210071000': {'2013-03-05': 245000, '2004-07-23': 249000},
'210074000': {'2015-02-02': 250000, '2005-10-18': 285000},
'210075000': {'2011-01-31': 260000,
'2010-03-22': 256000,
'2016-03-25': 264000,
'2020-03-18': 357000},
'210078000': {'2005-09-30': 280000, '2012-01-26': 230000},
'210084000': {'2003-12-29': 339000,
'2013-06-14': 385000,
'2018-05-14': 555000},
'210086000': {'2005-05-31': 350000,
'2002-01-04': 205000,
'2015-04-02': 385000,
'2008-05-21': 400000},
'210091100': {'2007-08-28': 265000, '2017-03-31': 290000},
'210092200': {'2017-04-17': 194000,
'2009-08-04': 174000,
'2019-08-27': 227360},
'210096000': {'2004-12-03': 415000,
'2007-03-29': 357000,
'2019-01-22': 440000},
'210100000': {'2017-07-05': 259500,
'2007-12-04': 235000,
'2004-07-27': 234500},
'210101000': {'2017-12-21': 500000, '2013-07-17': 314000},
'210102000': {'2007-02-26': 280000, '2017-12-27': 380500},
'210102100': {'2019-09-03': 519000,
'2012-12-27': 435000,
'2006-03-31': 125000,
'2009-12-04': 65000,
'2009-01-21': 125000,
'2005-08-12': 112000},
'210104000': {'2008-06-16': 285000,
'2007-10-05': 250000,
'2014-01-16': 230000},
'210104400': {'2003-07-15': 165000,
'2000-06-19': 110000,
'2007-03-28': 250000},
'210105100': {'2014-12-19': 210000, '2018-08-30': 315000},
'210105101': {'2013-05-31': 235000,
'2005-03-21': 250000,
'2000-07-18': 110000},
'210105110': {'2008-09-08': 220000, '2001-12-27': 133000},
'210105190': {'2000-11-29': 108250, '2018-11-14': 250000},
'210105210': {'2004-08-03': 214000, '2011-05-25': 180000},
'210105260': {'2003-06-13': 226000,
'2012-07-02': 266250,
'2005-04-04': 267500,
'2001-06-11': 120000},
'210105400': {'2012-11-19': 160000, '2002-07-11': 150000},
'210106000': {'2013-10-31': 255000, '2014-11-04': 263000},
'210107000': {'2008-05-01': 220000,
'2019-04-03': 270000,
'2004-07-13': 200000},
'210120000': {'2010-05-20': 376000, '2002-04-02': 136900},
'210123000': {'2002-10-16': 222000, '2000-09-25': 142500},
'210125100': {'2005-07-27': 309000,
'2015-06-04': 345000,
'2002-01-31': 165000,
'2009-08-11': 335000},
'210126000': {'2018-09-26': 442000, '2003-08-13': 298200},
'210129000': {'2016-03-21': 173775, '2015-07-14': 483881},
'210131200': {'2011-09-22': 1500, '2012-05-02': 355000},
'210131300': {'2012-07-26': 356000,
'2010-03-31': 345000,
'2016-11-02': 430000},
'21A001100': {'2017-10-02': 166500, '2017-03-03': 119500},
'21A0011A0': {'2007-02-28': 159000, '2003-12-18': 99500},
'21A001200': {'2006-07-31': 218000, '2015-05-29': 174900},
'21A001400': {'2019-09-24': 200000, '2017-08-02': 185900},
'21A0017A0': {'2010-06-22': 135000, '2003-06-18': 101000},
'21A001800': {'2001-08-31': 96000, '2019-06-20': 175000},
'21A003000': {'2004-05-17': 210000, '2011-07-19': 206500},
'21A004000': {'2004-05-04': 186000,
'2018-03-05': 255000,
'2017-10-27': 200000},
'21A005000': {'2000-03-20': 115000, '2018-06-20': 250000},
'21A009000': {'2007-11-30': 220000,
'2001-07-11': 133000,
'2012-05-25': 215000},
'21A012000': {'2003-09-03': 164300, '2009-09-24': 245000},
'21A014000': {'2017-07-05': 300000,
'2010-08-02': 289900,
'2001-12-14': 166000},
'21A019000': {'2014-06-17': 239000,
'2009-06-02': 235500,
'2019-05-23': 310000,
'2004-05-18': 172500},
'21A020000': {'2002-06-28': 185000, '2003-07-01': 211000},
'21A021000': {'2004-09-16': 95000, '2000-04-28': 115000},
'21A022000': {'2013-12-20': 277500, '2010-09-24': 267000},
'21A023000': {'2018-06-18': 377000, '2012-11-14': 166500},
'21A027000': {'2019-06-18': 302900, '2018-11-26': 175000},
'21A028000': {'2004-08-17': 240000,
'2020-05-01': 325200,
'2005-08-22': 259000,
'2016-04-28': 295000},
'21A031000': {'2008-08-01': 252500,
'2005-04-22': 247000,
'2017-06-15': 285000},
'21A035000': {'2004-11-04': 200500,
'2017-11-07': 269000,
'2003-06-02': 157100,
'2007-06-27': 259000},
'21A037000': {'2009-07-09': 239000,
'2005-04-04': 215000,
'2003-06-04': 175000},
'21A038000': {'2007-05-22': 235000, '2017-04-25': 275000},
'21A039000': {'2006-07-21': 224300,
'2010-09-16': 175521,
'2018-03-29': 375000},
'21A042000': {'2009-07-06': 257000, '2002-07-16': 171000},
'21A048000': {'2014-05-22': 244000,
'2009-05-29': 229500,
'2018-05-11': 327000,
'2002-10-25': 180000},
'21A055000': {'2001-04-03': 150000, '2007-04-16': 168750},
'21A057000': {'2014-03-26': 249750, '2002-06-20': 169800},
'21A059000': {'2009-03-24': 210000, '2013-06-18': 248000},
'21A064000': {'2014-03-07': 227500, '2016-05-16': 280000},
'21A068000': {'2002-05-31': 160000,
'2018-01-26': 341000,
'2010-08-31': 240500,
'2015-10-27': 294000},
'21A069000': {'2018-05-31': 375000,
'2015-05-26': 292000,
'2009-08-18': 180368,
'2010-03-30': 135900,
'2011-06-30': 258000},
'21A070000': {'2015-11-23': 317500,
'2010-04-08': 255000,
'2014-08-13': 281200},
'21A071000': {'2019-05-13': 250000, '2020-04-15': 322000},
'21A073000': {'2015-09-21': 219000, '2011-07-07': 205000},
'21A076000': {'2005-05-24': 247000, '2007-11-28': 225000},
'21A085000': {'2001-11-19': 182500, '2003-08-20': 190000},
'21A087000': {'2001-12-06': 138500,
'2015-07-17': 325000,
'2012-10-01': 320000,
'2006-07-24': 325500},
'21A087100': {'2007-05-01': 107500,
'2001-12-06': 138500,
'2009-04-09': 368532,
'2009-09-25': 350000},
'21A087800': {'2012-03-29': 250000, '2017-10-10': 325000},
'21A090000': {'2013-06-04': 425000,
'2013-01-08': 225000,
'2010-07-12': 225000},
'21A090100': {'2012-11-15': 450000,
'2013-08-01': 368600,
'2015-11-02': 385000},
'21A090200': {'2013-08-02': 376937, '2012-11-15': 450000},
'21A090300': {'2014-05-27': 379900,
'2012-11-15': 450000,
'2017-06-23': 475000},
'21A090400': {'2012-11-15': 450000, '2013-12-17': 465000},
'21A090500': {'2013-12-31': 450000,
'2012-11-15': 450000,
'2018-03-29': 596000},
'21A090600': {'2012-11-15': 450000,
'2014-04-03': 455000,
'2019-05-06': 605000},
'21A090700': {'2014-02-21': 457731, '2012-11-15': 450000},
'21A090800': {'2013-10-15': 374900, '2012-11-15': 450000},
'21A090900': {'2012-11-15': 450000, '2014-02-06': 330890},
'21A092000': {'2012-11-02': 85000, '2013-06-05': 289900},
'21A092100': {'2012-11-02': 85000,
'2013-04-29': 273200,
'2018-08-01': 359700},
'21A094000': {'2019-01-25': 282500, '2018-09-13': 245000},
'21A095000': {'2004-01-30': 127000,
'2019-12-10': 283000,
'2013-11-18': 220000,
'2004-11-18': 224000},
'21A095A00': {'2013-07-18': 355000, '2004-02-17': 39500},
'21A098000': {'2017-12-08': 220000, '2018-08-21': 293000},
'21A099100': {'2013-06-12': 273000,
'2017-06-12': 314900,
'2009-12-18': 240000},
'21A099130': {'2004-05-12': 192500,
'2010-10-15': 219000,
'2016-12-14': 310000},
'21A099180': {'2019-03-27': 203000,
'2002-04-02': 99500,
'2002-12-16': 134000},
'21A099210': {'2000-06-13': 93500, '2014-08-21': 141000},
'21A099220': {'2018-08-13': 210000, '2005-02-01': 120000},
...}
Below are a collection of functions to determine properties of sale histories. The first, strictincr(saleHistory), determines if a given parcel's sale history has been strictly increasing. This means that, sorting sale amount from earliest to latest, the sale amounts will be increasing.
The second, finalincr(saleHistory), determines if a given parcel's sale history has gone up via the first and last sales. This will return true as long as the latest sale is larger than the earliest.
def strictincr(saleHistory):
strictinc = {}
for parcel in saleHistory:
temp = pd.DataFrame.from_dict(saleHistory[parcel],orient='index')
temp = temp.sort_index(ascending=True)
curr = 0
datesOrd = temp.index.tolist()
strictinc[parcel] = True
for saleday in datesOrd:
if curr < saleHistory[parcel][saleday]:
curr = saleHistory[parcel][saleday]
else:
strictinc[parcel] = False
break
return strictinc
def finalincr(saleHistory):
finalinc = {}
for parcel in saleHistory:
temp = pd.DataFrame.from_dict(saleHistory[parcel],orient='index')
temp = temp.sort_index(ascending=True)
datesOrd = temp.index.tolist()
finalinc[parcel]= True
firstsale = saleHistory[parcel][datesOrd[0]]
lastsale = saleHistory[parcel][datesOrd[len(datesOrd)-1]]
if firstsale > lastsale:
finalinc[parcel] = False
return finalinc
Two series, strictTab and finalTab are created from the two functions above. One is a boolean series seeing if a parcel has strictly increased in price while the other just checks if the last is greater than the first.
strictTab = pd.DataFrame.from_dict(strictincr(saleHistory),orient='index',columns=['strictincr'])
finalTab = pd.DataFrame.from_dict(finalincr(saleHistory),orient='index',columns=['finalincr'])
print("Proportion of Parcels that increased in price every subsequent sale: "+ str(round(strictTab.sum()[0]/len(saleHistory),5)))
print("Proportion of Parcels that increased in price from first to last sale: "+ str(round(finalTab.sum()[0]/len(saleHistory),5)))
Proportion of Parcels that increased in price every subsequent sale: 0.62706 Proportion of Parcels that increased in price from first to last sale: 0.78668
The above functions can be adapted into applied functions that will produce a series that shows percent change in between the first and last sales of a parcel. incrPercent(row,saleHistory) will divide the difference between the last and first sales by the first sale and multiply by 100 to produce a percentage. incrPercentDay(row,saleHistory) will perform the same calculation, but will divide the value by the number of days elapsed between the two sales. This serves to normalize for time.
def incrPercent(row,saleHistory):
temp = pd.DataFrame.from_dict(saleHistory[row.name],orient='index')
temp = temp.sort_index(ascending=True)
datesOrd = temp.index.tolist()
firstsale = saleHistory[row.name][datesOrd[0]]
lastsale = saleHistory[row.name][datesOrd[len(datesOrd)-1]]
val = (lastsale-firstsale)/firstsale * 100
return val
def incrPercentDay(row,saleHistory):
temp = pd.DataFrame.from_dict(saleHistory[row.name],orient='index')
temp = temp.sort_index(ascending=True)
datesOrd = temp.index.tolist()
firstdate = datetime.datetime.strptime(datesOrd[0], "%Y-%m-%d").date()
lastdate = datetime.datetime.strptime(datesOrd[len(datesOrd)-1], "%Y-%m-%d").date()
diff = (lastdate-firstdate).days
firstsale = saleHistory[row.name][datesOrd[0]]
lastsale = saleHistory[row.name][datesOrd[len(datesOrd)-1]]
return (lastsale-firstsale)/firstsale * 100/diff
Since finalTab from section 4.1 is a Series that contains which parcels have increased from first to last sale, it is fed into the two percent change functions and the resulting two Series are added to the DataFrame. It is then merged with the use-all DataFrame after the sale dates before 2000 have been filtered out, geoSalesResid, to produce geoSalesResidPercent.
percentTab = pd.DataFrame(finalTab[finalTab.finalincr])
percentTab.insert(percentTab.shape[1],'perc',finalTab[finalTab.finalincr].apply(incrPercent,axis=1,args=(saleHistory,)))
percentTab.insert(percentTab.shape[1],'percNorm',finalTab[finalTab.finalincr].apply(incrPercentDay,axis=1,args=(saleHistory,)))
geoSalesResidPercent = pd.merge(makeRecentSales('1999-12-31'),percentTab,left_on='ParcelNumber', right_index=True).drop_duplicates('ParcelNumber')
geoSalesResidPercent
| RecordID_Int | ParcelNumber | SaleDate | SaleAmount | Unit | Address | UseCode | LATITUDE | LONGITUDE | finalincr | perc | percNorm | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 24 | 86 | 010017000 | 2017-06-08 | 636000 | NaN | 1893 WESTVIEW RD | Single Family-1 Conversion | 38.050093 | -78.499944 | True | 61.949686 | 0.124397 |
| 31 | 93 | 010017100 | 2000-09-14 | 459500 | NaN | 1895 WESTVIEW RD | Single Family-2 Conversion | 38.050473 | -78.499333 | True | 42.491839 | 0.022566 |
| 36 | 99 | 010019000 | 2002-05-23 | 85000 | NaN | 1890 WESTVIEW RD | Single Family | 38.050086 | -78.500280 | True | 684.285714 | 0.276927 |
| 43 | 106 | 010020000 | 2010-08-04 | 962500 | NaN | 1888 WESTVIEW RD | Single Family | 38.049628 | -78.500349 | True | 250.000000 | 0.071860 |
| 62 | 125 | 010024A00 | 2002-06-12 | 125000 | NaN | 1876 WESTVIEW RD | Single Family | 38.048490 | -78.499412 | True | 652.000000 | 0.148148 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 47995 | 56251 | 610313000 | 2001-03-23 | 83000 | NaN | 100 MILFORD TER | Single Family Attached | 38.018602 | -78.470619 | True | 116.746988 | 0.074790 |
| 48000 | 56256 | 610314000 | 2002-07-31 | 117900 | NaN | 102 MILFORD TER | Single Family Attached | 38.018528 | -78.470644 | True | 6.022053 | 0.001819 |
| 48005 | 56261 | 610315000 | 2005-11-01 | 174900 | NaN | 104 MILFORD TER | Single Family Attached | 38.018473 | -78.470647 | True | 6.346484 | 0.007005 |
| 48013 | 56269 | 610317000 | 2009-12-14 | 102128 | NaN | 108 MILFORD TER | Single Family Attached | 38.018362 | -78.470659 | True | 19.947517 | 0.189976 |
| 48019 | 56275 | 610318000 | 2001-07-20 | 89900 | NaN | 110 MILFORD TER | Single Family Attached | 38.018268 | -78.470688 | True | 64.627364 | 0.011320 |
3780 rows × 12 columns
New color functions have to be chosen as the numerical values differ from sales prices.
def chooseColorPerc(sale):
if sale < 5:
return '#fef0d9'
elif 5 <= sale < 10:
return '#fdd49e'
elif 20 <= sale < 50:
return '#fdbb84'
elif 50 <= sale < 100:
return '#fc8d59'
elif 100 <= sale < 200:
return '#e34a33'
else:
return '#b30000'
def chooseColorPercNorm(sale):
if sale < 0.01:
return '#fef0d9'
elif 0.01 <= sale < 0.05:
return '#fdd49e'
elif 0.05 <= sale < 0.10:
return '#fdbb84'
elif 0.10 <= sale < 0.25:
return '#fc8d59'
elif 0.25 <= sale < 0.5:
return '#e34a33'
else:
return '#b30000'
def addMarkersPerc(row,currmap,norm,group):
# not normalized to day (perc)
if not norm:
line = str(row['perc']) + '%'
marker = folium.CircleMarker(location=[row['LATITUDE'],row['LONGITUDE']],
popup=line,radius=1.5,color=chooseColorPerc(row['perc']),
fill_color=chooseColorPerc(row['perc']))
# normalized to day (percNorm)
else:
line = str(row['percNorm']) + '%'
marker = folium.CircleMarker(location=[row['LATITUDE'],row['LONGITUDE']],
popup=line,radius=1.5,color=chooseColorPercNorm(row['percNorm']),
fill_color=chooseColorPercNorm(row['percNorm']))
group.add_child(marker)
return
The generatePercentMap(row,currmap,group) function will do the following:
def generateMapPercent(table):
# 1. Import the DataFrame that classifies each house to a region
classifiedregions = pd.read_csv('data/salesLast20Years.csv')
classifiedregions = classifiedregions.loc[:,['ParcelNumber','Region']]
classifiedregions = classifiedregions.drop_duplicates(subset='ParcelNumber')
# 2. Merge with the one that has the percent change in sales
table = table.merge(classifiedregions,left_on='ParcelNumber',right_on='ParcelNumber')
# 3. Group by region number and find average sale amount and median in each, even for region 0, then make a DataFrame
# with region numbers as a column
percByRegionAvg = produceAGroup(table,'mean','perc')
percByRegionMed = produceAGroup(table,'median','perc')
percNormByRegionAvg = produceAGroup(table,'mean','percNorm')
percNormByRegionMed = produceAGroup(table,'median','percNorm')
countByRegion = produceAGroup(table,'count','-')
# 4. load GeoJSON file of regions, adding the SaleAverage as a property to each
with open ("data/regions.geojson") as f:
regionlayer = json.load(f)
count = 1
for i in regionlayer['features']:
i['properties']['% DIFF AVG'] = checkIfZero(percByRegionAvg, 'Avg',count)
i['properties']['% DIFF MED'] = checkIfZero(percByRegionMed, 'Med',count)
i['properties']['% NORM DIFF AVG'] = checkIfZero(percNormByRegionAvg, 'Avg',count)
i['properties']['% NORM DIFF MED'] = checkIfZero(percNormByRegionMed, 'Med',count)
i['properties']['NUM PARCELS'] = checkIfZero(countByRegion, 'Count',count)
count += 1
# 5. Load GeoJSON of city boundary, adding SaleAverage/Median of whole city to the Layer
with open ("data/cityboundary.geojson") as f:
citylayer = json.load(f)
citylayer['features'][0]['properties']['% DIFF AVG'] = str(round(table.perc.mean(),2)) + '%'
citylayer['features'][0]['properties']['% DIFF MED'] = str(round(table.perc.median(),2)) + '%'
citylayer['features'][0]['properties']['% NORM DIFF AVG'] = str(round(table.percNorm.mean(),2)) + '%'
citylayer['features'][0]['properties']['% NORM DIFF MED'] = str(round(table.percNorm.median(),2)) + '%'
citylayer['features'][0]['properties']['NUM PARCELS'] = str(table.perc.mean()) + '%'
# 6. Create Folium Map and add TileLayer Options
m = plugins.DualMap(location=city_cville_coord, zoom_start=13)
folium.raster_layers.TileLayer('Open Street Map').add_to(m)
folium.raster_layers.TileLayer('Stamen Toner').add_to(m)
folium.raster_layers.TileLayer('CartoDB Positron').add_to(m)
# 7. Group: GeoJson of Charlottesville City Boundary
cityavg = table.perc.mean()
style_function1 = lambda x: {'fillColor': chooseColorPerc(cityavg), 'color': chooseColorPerc(cityavg)}
city1 = folium.GeoJson(citylayer, name='City of Charlottesville Boundary',
style_function=style_function1,
tooltip=folium.GeoJsonTooltip(['NAME','% DIFF AVG','% DIFF MED'])).add_to(m.m1)
cityavgnorm = table.percNorm.mean()
style_function2 = lambda x: {'fillColor': chooseColorPercNorm(cityavgnorm), 'color': chooseColorPercNorm(cityavgnorm)}
city2 = folium.GeoJson(citylayer, name='City of Charlottesville Boundary',
style_function=style_function2,
tooltip=folium.GeoJsonTooltip(['NAME','% NORM DIFF AVG','% NORM DIFF MED'])).add_to(m.m2)
# 8. Group: Addition of Markers for each individual Parcel
percMarkers = folium.FeatureGroup(name='% Diff Markers',show=False)
table.apply(addMarkersPerc,axis=1,args=(m.m1,False,percMarkers))
percMarkers.add_to(m.m1)
percMarkersNorm = folium.FeatureGroup(name='% Diff Norm Markers',show=False)
table.apply(addMarkersPerc,axis=1,args=(m.m2,True,percMarkersNorm))
percMarkersNorm.add_to(m.m2)
# 9. Group: Choropleth of Planning Neighborhood Regions' Average Sale
choroPerc1 = folium.Choropleth(geo_data=regionlayer, key_on='feature.properties.OBJECTID',
data=percByRegionAvg,columns=['Regions','Avg'],
fill_color='YlOrRd',legend_name='% Diff',name='% Diff Average', show=False,
highlight=True).add_to(m.m1)
choroPerc1.geojson.add_child(folium.GeoJsonTooltip(['OBJECTID','NAME','% DIFF AVG','NUM PARCELS']))
choroPercNorm1 = folium.Choropleth(geo_data=regionlayer, key_on='feature.properties.OBJECTID',
data=percNormByRegionAvg,columns=['Regions','Avg'],
fill_color='YlOrRd',legend_name='% Norm Diff',name='% Norm Diff Average', show=False,
highlight=True).add_to(m.m2)
choroPercNorm1.geojson.add_child(folium.GeoJsonTooltip(['OBJECTID','NAME','% NORM DIFF AVG','NUM PARCELS']))
# 10. Group: Choropleth of Planning Neighborhood Regions' Median Sale
choroPerc2 = folium.Choropleth(geo_data=regionlayer, key_on='feature.properties.OBJECTID',
data=percByRegionMed,columns=['Regions','Med'],
fill_color='YlOrRd',legend_name='% Diff',name='% Diff Median', show=False,
highlight=True).add_to(m.m1)
choroPerc2.geojson.add_child(folium.GeoJsonTooltip(['OBJECTID','NAME','% DIFF MED','NUM PARCELS']))
choroPercNorm2 = folium.Choropleth(geo_data=regionlayer, key_on='feature.properties.OBJECTID',
data=percNormByRegionMed,columns=['Regions','Med'],
fill_color='YlOrRd',legend_name='% Norm Diff',name='% Norm Diff Median', show=False,
highlight=True).add_to(m.m2)
choroPercNorm2.geojson.add_child(folium.GeoJsonTooltip(['OBJECTID','NAME','% NORM DIFF MED','NUM PARCELS']))
# 11. Add LayerControl and Save Map
folium.LayerControl().add_to(m)
m.save("figures/map-percentchange.html")
return
generateMapPercent(geoSalesResidPercent.copy())